在学习这门课的时候,实现各种功能时进行了各种配置。我想将各种配置综合讲述一下。

首先自定义配置类,需要继承WebSecurityConfigurerAdapter这个类。

在这个类里面做了一些默认配置

1
2
3
4
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
....
}

更改PasswordEncoder的实现为BCryptPasswordEncoder

1
2
3
4
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

注入AuthenticationManager,用它的方法进行认证

authenticationManager.authenticate()

1
2
3
4
5
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

安全过滤器链配置方法HttpSecurity http配置方法

默认配置

1
2
3
4
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}

super.configure(http);使用了父类中一些默认配置。查看源代码

1
2
3
4
5
6
7
protected void configure(HttpSecurity http) throws Exception {
((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated()
.and()).
formLogin()
.and())
.httpBasic();
}

http.authorizeRequests().anyRequest().authenticated()表示对任何请求都要进行权限认证

http.formLogin(),加入了UsernamePasswordAuthenticationFilter过滤器。

默认配置:

  • 所有的请求访问都需要被授权。
  • 使用 form 表单进行登陆(默认路径为/login),也就是前几篇我们见到的登录页。
  • 防止 CSRF 攻击、XSS攻击。
  • 启用 HTTP Basic 认证

自定义配置

关闭防止csrf攻击

1
http.csrf().disable()

为什么要关闭防止csrf攻击?

CSRF是指跨站请求伪造(Cross-site request forgery),是web常见的攻击之一。

https://blog.csdn.net/freeking101/article/details/86537087

SpringSecurity去防止CSRF攻击的方式就是通过csrf_token。后端会生成一个csrf_token,前端发起请求的时候需要携带这个csrf_token,后端会有过滤器进行校验,如果没有携带或者是伪造的就不允许访问。

但是在前后端分离的项目中我们的认证信息其实是token,而token并不是存储中cookie中,并且需要前端代码去把token设置到请求头中才可以,所以CSRF攻击也就不用担心了。

因此使用token是天然防止csrf攻击的。

不通过Session获取SecurityContext

1
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

权限配置

1
2
http.authorizeRequests().antMatchers("/user/login").anonymous()
.anyRequest().authenticated();

对于登录接口 允许匿名访问除上面外的所有请求全部需要鉴权认证

参考链接:

https://blog.csdn.net/qq_41865652/article/details/123685248

https://blog.csdn.net/Shair911/article/details/104181917/

http.authorizeRequests()主要是对url进行访问权限控制,通过这个方法来实现url授权操作。

  • anyRequest(),表示匹配所有的url请求

    1
    2
    3
    http.authorizeRequests()
    // 匹配所有的请求,并且所有请求都需要登录认证
    .anyRequest().authenticated();
  • antMatcher(String regx),传递一个ant表达式参数,表示匹配所有满足ant表达式的请求

    • ant表达式中特殊字符解释

      规则 解释说明
      匹配一个字符
      * 匹配0个或多个字符
      ** 匹配0个或多个目录

      配置类代码示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      http.authorizeRequests()
      // 允许登录页面匿名访问
      .antMatchers("/showLogin", "/errPage").anonymous()
      // 所有的静态资源允许匿名访问
      .antMatchers(
      "/css/**",
      "/js/**",
      "/images/**",
      "/fonts/**",
      "/favicon.ico"
      ).anonymous()
      // 其他所有的请求都需要登录认证
      .anyRequest().authenticated();
  • antMatcher(HttpMethod.*, String regx),传递一个请求方法类型参数加ant表达式参数,表示匹配所有满足ant表达式的指定请求方式的url

    请求方式的枚举类如下:

    image-20220822175402125

    配置类代码示例:

    1
    2
    3
    http.authorizeRequests()
    // 允许GET请求登录页面匿名访问
    .antMatchers(HttpMethod.GET, "/showLogin", "/errPage").anonymous();

访问控制方法

方法名称 方法作用
permitAll() 表示所匹配的URL任何人都允许访问
anonymous() 表示可以匿名访问匹配的URL。和permitAll()效果类似,只是设置为anonymous()的url会执行filterChain中的filter
denyAll() 表示所匹配的URL都不允许被访问。
authenticated() 表示所匹配的URL都需要被认证才能访问
rememberMe() 允许通过remember-me登录的用户访问
access() SpringEl表达式结果为true时可以访问
fullyAuthenticated() 用户完全认证可以访问(非remember-me下自动登录)
hasRole() 如果有参数,参数表示角色,则其角色可以访问
hasAnyRole() 如果有参数,参数表示角色,则其中任何一个角色可以访问
hasAuthority() 如果有参数,参数表示权限,则其权限可以访问
hasAnyAuthority() 如果有参数,参数表示权限,则其中任何一个权限可以访问
hasIpAddress() 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问

配置案例示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//任何用户都可以访问
http.authorizeRequests().antMatchers("/index").permitAll();
http.authorizeRequests().antMatchers("/index").access("permitAll");]

//任何用户都不能访问
http.authorizeRequests().antMatchers("/home").denyAll();
http.authorizeRequests().antMatchers("/home").access("denyAll");

//认证用户可以访问(除了匿名认证)
http.authorizeRequests().antMatchers("/admin").authenticated();
http.authorizeRequests().antMatchers("/admin").access("authenticated");

//认证用户可以访问(除了匿名认证,记住我)
http.authorizeRequests().antMatchers("/admin").fullyAuthenticated();
http.authorizeRequests().antMatchers("/admin").access("fullyAuthenticated");

//记住我的认证可以访问
http.authorizeRequests().antMatchers("/admin").rememberMe();
http.authorizeRequests().antMatchers("/admin").access("rememberMe");

//匿名用户可以访问
http.authorizeRequests().antMatchers("/admin").anonymous();
http.authorizeRequests().antMatchers("/admin").access("anonymous");

//是否有权限
http.authorizeRequests().antMatchers("/index").hasAuthority("user");
http.authorizeRequests().antMatchers("/index").access("hasAuthority('user')");

//是否有任意一个权限
http.authorizeRequests().antMatchers("/home").hasAnyAuthority("update", "delete", "insert");
http.authorizeRequests().antMatchers("/home").access("hasAnyAuthority('update','delete','insert')");

//spring security中的role并非是一个或多个权限的集合,而是权限的一种,通常以ROLE_开头
//role就是ROLE_开头的权限
//注意:hasRole、hasAnyRole里面的role不需要以ROLE_开头,否则会报异常
//注意:如果在access里面使用hasRole、hasAnyRole则ROLE_前缀可加,可不加
http.authorizeRequests().antMatchers("/index").hasRole("GUEST");
http.authorizeRequests().antMatchers("/index").access("hasRole('GUEST')");
http.authorizeRequests().antMatchers("/admin").hasAuthority("ROLE_GUEST");
http.authorizeRequests().antMatchers("/home").hasAnyRole("GUEST", "USER", "ADMIN");
http.authorizeRequests().antMatchers("/home").access("hasAnyRole('ROLE_GUEST','ROLE_USER','ROLE_ADMIN')");

添加过滤器到过滤器链

先将获取过滤器

1
2
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

然后配置到过滤器链中

1
2
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//将jwtAuthenticationTokenFilter 过滤器放到UsernamePasswordAuthenticationFilter过滤器前。
  • addFilterBefore():将过滤器放到 **过滤器之前。

  • addFilter():将过滤器放到过滤器链最后。

  • addFilterAfter():将过滤器放到 **过滤器之后。

    1
    2
    http.addFilterAfter(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    //将jwtAuthenticationTokenFilter 过滤器放到UsernamePasswordAuthenticationFilter过滤器后。
  • addFilterAt:将过滤器放在**过滤的位置。并不是取代。

配置异常处理器

先获取异常处理器

1
2
3
4
5
@Autowired
private AccessDeniedHandler accessDeniedHandler;

@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;

然后进行配置。

1
2
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);

表单登录配置

取消默认的自带的表单登录配置

1
http.formLogin().disable();

配置演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootConfiguration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()//开启formLogin默认配置
.loginPage("/login/auth").permitAll()//请求时未登录跳转接口
.failureUrl("/login/fail")//用户密码错误跳转接口
.defaultSuccessUrl("/login/success",true)//登录成功跳转接口
.loginProcessingUrl("/login")//post登录接口,登录验证由系统实现
.usernameParameter("username") //要认证的用户参数名,默认username
.passwordParameter("password") //要认证的密码参数名,默认password
.and()
.logout()//配置注销
.logoutUrl("/logout")//注销接口
.logoutSuccessUrl("/login/logout").permitAll()//注销成功跳转接口
.deleteCookies("myCookie") //删除自定义的cookie
.and()
.csrf().disable(); //禁用csrf
}
}

使用formLogin()就会添加如下过滤器

  • UsernamePasswordAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter

添加认证成功处理器和认证失败处理器

先获取认证成功处理器和认证失败处理器。

1
2
3
4
5
@Autowired
private AuthenticationSuccessHandler successHandler;

@Autowired
private AuthenticationFailureHandler failureHandler;

然后再进行配置

1
2
3
4
5
 http.formLogin()
// 配置认证成功处理器
.successHandler(successHandler)
// 配置认证失败处理器
.failureHandler(failureHandler);

总的配置代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

@Autowired
private AccessDeniedHandler accessDeniedHandler;

@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;

@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {

http
//关闭csrf
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问
.antMatchers("/user/login").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();

http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

//配置异常处理器。
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);

http.authorizeRequests().antMatchers().hasAuthority("system:test:index");


}
}

__END__