SpringSecurity学习笔记(一)


保护web应用的安全----参考的详细教程,点击这里,感谢大佬!
Spring Security(一)–Architecture Overview----参考的详细教程,点击这里,感谢大佬!
Spring Security(二)–Guides----参考的详细教程,点击这里,感谢大佬!
Spring Security(三)–核心配置解读----参考的详细教程,点击这里,感谢大佬!
Spring Security(四)–核心过滤器源码分析----参考的详细教程,点击这里,感谢大佬!
Spring Security(五)–动手实现一个IP_Login----参考的详细教程,点击这里,感谢大佬!

注:鄙人不才,此篇文章约等于搬运,写此文章的目的一是为了加深学习印象,二是希望能通过发布文章的方式来督促自己,建议大家直接看上述链接中的原教程,讲解更加通俗易懂。

对于任何一门技术都无耻的保持着得过且过的心态,多年后发现,基础根基不牢,只会在错误的道路上渐行渐远。。。
幡然醒悟,一定痛改前非,自我救赎就从这篇SpringSecurity开始!

感谢互联网时代,让每一门技术在网上都有教程可循!
感谢技术大佬们的无私分享!

一、SpringSecurity是什么

众所周知,SpringSecurity是Spring的一个安全框架,核心功能为认证(你是谁)、授权(你能干点啥)、攻击防护(防止伪造身份),具体如何使用呢,请慢慢看~

二、安装SpringSecurity

maven项目下,直接在pom.xml中引入依赖即可,代码入下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.1.RELEASE</version>
        </dependency>

三、核心组件

1、SecurityContextHolder

SecurityContextHolder用于存储安全上下文(security content)的信息。当前操作的用户是谁,拥有哪些权限,是否已经被认证了,都被保存在这里。SecurityContextHolder默认使用 ThreadLocal (JDK提供的,保证多线程安全的方法) 策略来存储认证信息。

因为身份信息存储于SecurityContextHolder中,并且与线程是绑定的,所以我们可以在程序的任何地方,通过静态方法,来获取用户的身份信息。代码举例如下

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//getAuthentication()返回了认证信息,getPrincipal()返回了身份信息
if (principal instanceof UserDetails) {
//UserDetails是SpringSecurity封装的一个身份信息的接口
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

2、Authentication

接口源码:

//Authentication是SpringSecurity中最高级别的身份/认证的抽象
public interface Authentication extends Principal, Serializable {

    //权限信息列表
    Collection<? extends GrantedAuthority> getAuthorities();

    //密码信息,用户输入的密码字符串,在认证后通常会被移除
    Object getCredentials();

    //细节信息,它记录了访问者的ip地址和sessionId的值。
    Object getDetails();

    //最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一
    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

SpringSecurity身份验证过程:

  1. 认证拦截器获取到用户的登录账号和密码,封装成Authentication,通常是UsernamePasswordAuthenticationToken
  2. AuthenticationManager身份认证器对Authentication进行认证,认证通过后,返回一个带着信息(权限信息,身份信息,细节信息,但密码通常会被移除)的Authentication实例。
  3. SecurityContextHolder安全上下文容器将第2步中填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…);方法,设置到其中。

3、AuthenticationManager
在一个项目中,登录方式可以有多种:账号+密码登录、手机号+验证码登录、邮箱+密码登录等等,所以AuthenticationManager是不会进行直接验证的,其接口的常用实现类ProviderManager内部会维护一个List< AuthenticationProvider>列表。账号+密码登录、手机号+验证码登录、邮箱+密码登录对应着三个AuthenticationProvider,最终都要通过AuthenticationManager,也就是身份认证管理器中进行验证逐一验证。

4、DaoAuthenticationProvider
Dao,顾名思义,就是数据库访问层
获取用户提交的用户名密码,比对其与数据库中已经存储的用户名密码,如果正确,返回数据库中的用户信息

5、UserDetails和UserDetailsService

UserDetails代表了用户的详细信息,代码如下

public interface UserDetails extends Serializable {

   Collection<? extends GrantedAuthority> getAuthorities();

   String getPassword();

   String getUsername();

   boolean isAccountNonExpired();

   boolean isAccountNonLocked();

   boolean isCredentialsNonExpired();

   boolean isEnabled();
}

UserDetails与Authentication有很多相似之处,但如何区分两者是很重要的!
Authentication中的getCredentials与UserDetails中的getPassword的区别在于,前者是用户提交的,后者是数据库中取出来的,认证器实际上就是将这两者进行比对。
Authentication中的getAuthorities实际上是由UserDetails的getAuthorities传递形成的,Authentication中的getUserDetails则是由AuthenticationManager认证后被填充的。

UserDetailsService负责从指定位置(通常是数据库)中取得用户信息,仅此而已,UserDetailsService常见的实现类有JdbcDaoImpl,InMemoryUserDetailsManager,前者从数据库加载用户,后者从内存中加载用户,也可以自己实现UserDetailsService,通常这更加灵活。

在这里插入图片描述

四、核心配置解读

1、功能介绍

这是SpringSecurity入门指南中的配置项目

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http
          .authorizeRequests()
              .antMatchers("/", "/home").permitAll()
              .anyRequest().authenticated()
              .and()
          .formLogin()
              .loginPage("/login")
              .permitAll()
              .and()
          .logout()
              .permitAll();
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth
          .inMemoryAuthentication()
              .withUser("admin").password("admin").roles("USER");
  }
}

当配置了上述的javaConfig之后,我们的项目具有了以下功能:

  • 除了“/”,”/home”(首页),”/login”(登录),”/logout”(注销),之外,其他路径都需要认证。
  • 指定“/login”该路径为登录页面,当未认证的用户尝试访问任何受保护的资源时,都会跳转到“/login”。
  • 默认指定“/logout”为注销页面。
  • 配置一个内存中的用户认证器,使用admin/admin作为用户名和密码,具有USER角色。
  • 防止CSRF攻击。
  • Session Fixation protection(防止别人篡改sessionId)。
  • Security Header(添加一系列和Header相关的控制)
    (1)HTTP Strict Transport Security for secure requests
    (2)集成X-Content-Type-Options
    缓存控制
    (3)集成X-XSS-Protection.aspx)
    (4)X-Frame-Options integration to help prevent Clickjacking(iframe被默认禁止使用)
  • 为Servlet API集成了如下的几个方法
    (1)HttpServletRequest#getRemoteUser())
    (2)HttpServletRequest.html#getUserPrincipal())
    (3)HttpServletRequest.html#isUserInRole(java.lang.String))
    (4)HttpServletRequest.html#login(java.lang.String, java.lang.String))
    (5)HttpServletRequest.html#logout())

2、@EnableWebSecurity

组合注解:

@Import({ WebSecurityConfiguration.class, // <2>
      SpringWebMvcImportSelector.class }) // <1>
@EnableGlobalAuthentication // <3>
@Configuration
public @interface EnableWebSecurity {
   boolean debug() default false;
}

在自定义类中,我们同时使用了@EnableWebSecurity和WebSecurityConfigurerAdapter,那么显然,@EnableWebSecurity注解的作用更大。
@import是SpringBoot提供的用于引入外部配置的注解,可以理解为,是@EnableWebSecurity激活了@import注解中包含的配置类。

<1> SpringWebMvcImportSelector的作用是判断当前的环境是否包含springmvc,因为spring security可以在非spring环境下使用,为了避免DispatcherServlet的重复配置,所以使用了这个注解来区分。

<2> WebSecurityConfiguration顾名思义,是用来配置web安全的,下面会详细介绍。

<3> @EnableGlobalAuthentication注解的源码如下:

@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

重点仍是@import中的配置类,AuthenticationConfiguration用来配置认证相关的核心类。

综上所述,@EnableWebSecurity的核心功能就是加载了WebSecurityConfiguration和AuthenticationConfiguration两个配置类,也就是将SpringSecurity的职责分成了配置安全信息、配置认证信息两大部分。

WebSecurityConfiguration
在这个配置类中,有一个非常重要的Bean被注册了,

@Configuration
public class WebSecurityConfiguration {

    //DEFAULT_FILTER_NAME = "springSecurityFilterChain"
    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        ...
    }
 }

springSecurityFilterChain是SpringSecurity的核心过滤器,是整个认证的入口。WebSecurityConfiguration中完成了声明springSecurityFilterChain的作用,并且最终交给DelegatingFilterProxy这个代理类,负责拦截请求(注意DelegatingFilterProxy这个类不是spring security包中的,而是存在于web包中,spring使用了代理模式来实现安全过滤的解耦)。

AuthenticationConfiguration
AuthenticationConfiguration的主要作用,便是为了生成全局的身份认证管理器 AuthenticationManage。

3、WebSecurityConfigurerAdapter

Adapter:适配器模式–使得原本不能一起使用的两个类,通过适配器模式,在不覆盖无关源代码的前提下,可以一起使用。就像讲中文的人和讲英文的人之间进行对话,需要一个翻译一样。

WebSecurityConfigurerAdapter中,我们只关心需要重写的三个方法即可,如下
/在这里插入图片描述
由方法中的参数可得,我们接下来需要进行的是对AuthenticationManagerBuilder、WebSecurity、HttpSecurity的个性化配置。

HttpSecurity常用配置

@Configuration
@EnableWebSecurity
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/resources/**", "/signup", "/about").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .failureForwardUrl("/login?error")
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/index")
                .permitAll()
                .and()
            .httpBasic()
                .disable();
    }
}

上述代码是一个典型的HttpSecurity配置,在 四、核心配置解读->1、功能介绍中,已经详细解读,这里不再赘述。
.authorizeRequests()、 .formLogin()、 .logout()、.httpBasic()都代表的Http的相关安全配置,无一例外全部返回configure。而全部的Http配置,见下图:
在这里插入图片描述
我们只掌握SpringSecutiry中的自动配置就可以了,详细本人就不多做了解了。

WebSecurityBuilder

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
            .antMatchers("/resources/**");
    }
}

以大佬的经验,这个配置中并不会出现太多的配置信息。不多做了解。

AuthenticationManagerBuilder

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("admin").password("admin").roles("USER");
    }
}

想要在WebSecurityConfig 中进行认证,我们可以用configure(AuthenticationManagerBuilder auth)来暴露一个AuthenticationManager建造器:AuthenticationManagerBuilder

我们在上述文章中,配置方法并不是这样的,而是:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("admin").password("admin").roles("USER");
    }
}

如果你的应用只有一个WebSecurityConfigurerAdapter ,那么他们之间的差别可以忽略不计,如果有多个,则使用@Autowired注解注入的AuthenticationManagerBuilder则是全局身份认证器,可以跨多个WebSecurityConfigurerAdapter,以及影响到基于Method的安全控制;而 protected configure()的方式则类似于一个匿名内部类,它的作用域局限于一个WebSecurityConfigurerAdapter内部。

笔记(一),完


版权声明:本文为weixin_38530160原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。