SpringSecurity6详解
1. SpringSecurity6
前言:本文将基于Spring Boot 3.x依赖的Spring Security 6.x版本作为讲解。Spring Boot3.X是2022年11月正式发布的
因为Spring Boot2.x是依赖的Spring Security5.x,且5.x的security与6.x的变化较大【6.x引入了很多破坏性的更新,包括废弃代码的删除,方法重命名,全新的配置DSL等】。so,本文应运而生。
SpringSecurity:是一个高度自定义的安全框架,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大星重复代码的工作。
本质:是一个过滤器链,由多个过滤器组成。
1.1 回顾过滤器链
过滤器链:指的是将多个过滤器按照一定的顺序组织起来,确保它们能够按需执行。
我们演示一下springboot整合过滤器链。
// 配置的方式
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("进入日志过滤器...");
// 逻辑处理(例如记录请求信息)
chain.doFilter(request, response);
System.out.println("离开日志过滤器...");
}
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> getFilterRegistrationBean() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoggingFilter());
filterRegistrationBean.setOrder(1); // 用于指定过滤器的优先级
filterRegistrationBean.addUrlPatterns("/user/*");
filterRegistrationBean.setName("filter1");
return filterRegistrationBean;
}
// 如果有多个过滤器,则在后面继续注册
@Bean
public FilterRegistrationBean<Filter> getFilterRegistrationBean() {
}
}
//////////////////////////////////////////////////////////////////
// 注解的方式 直接用@Order注解给多个过滤器加上,实现过滤器链的效果
@Component
@Order(1) // 第一优先级
@WebFilter(urlPatterns = "/api/*") // 只对 /api/* 路径下的请求生效
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("First Filter");
chain.doFilter(request, response);
}
}
@Component
@Order(2) // 第二优先级
@WebFilter(urlPatterns = "/api/*") // 只对 /api/* 路径下的请求生效
public class SecondFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Second Filter");
chain.doFilter(request, response);
}
}
//////////////////////////////////////////////////////////////////
// 使用 FilterChain 的方法可以显式地按顺序调用多个过滤器。
public class CustomFilterChain implements Filter {
private final List<Filter> filters;
public CustomFilterChain() {
this.filters = new ArrayList<>();
this.filters.add(new FirstFilter());
this.filters.add(new SecondFilter());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterChain currentChain = chain;
for (Filter filter : filters) {
currentChain = new CustomFilterChainWrapper(currentChain, filter);
}
currentChain.doFilter(request, response);
}
}
2. SpringSecurity6与SpringSecurity5重大区别

2.1 gh-11923-移除WebSecurityConfigurerAdapter。使用SecurityFilterChain bean方式来代替
为什么这样做,springsecurity有解释:

这一条改变直接影响了 springboot2.x整合springsecurity5.x时的实现方式,WebSecurityConfigurerAdapter 被移除后,作为替代,我们需要创建类型为SecurityFilterChain的bean。
先来回顾5.x的写法:
/**
* WebSecurityConfigurerAdapter:继承该类是为了通过重写SpringBoot对SpringSecurity的默认配置。
* 它提供了一些方法,您可以重写这些方法来定义安全规则、配置身份验证和授权方式,以及定制其他与安全相关的行为。
*
* 常用方法:(我们重写这些方法,就能调整security的默认配置)
* configure(HttpSecurity http): 这是最重要的方法之一,用于配置如何通过拦截器保护HTTP请求。您可以定义哪些URL路径需要特定的安全配置,例如要求身份验证、授权规则和访问权限等。
* configure(AuthenticationManagerBuilder auth): 这个方法用于配置身份验证机制。您可以定义用户存储的位置、身份验证规则以及密码编码器等。
* configure(WebSecurity web): 这个方法用于配置Spring Security忽略特定的静态资源,例如CSS、JavaScript文件或其他不需要身份验证的静态资源。
* userDetailsService(): 这个方法返回一个UserDetailsService对象,用于从数据库或其他数据源加载用户信息。
**/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.formLogin() 是Spring Security中用于配置基于表单的身份验证的方法。
// 常用方法如下:(还有很多方法,自己写的时候通过 .方法 查看)
// loginPage(String loginPage): 指定登录页面的URL。如果不指定,则默认为/login
// loginProcessingUrl(String loginProcessingUrl): 指定登录表单提交的URL。默认为/login(也就是我们的Controller地址)
// usernameParameter(String usernameParameter): 指定登录表单中用户名字段的参数名。默认为username,如果你的表单提交的用户名密码不是这个,你就需要来特地指定
// passwordParameter(String passwordParameter): 指定登录表单中密码字段的参数名。默认为password,如果你的表单提交的用户名密码不是这个,你就需要来特地指定
// defaultSuccessUrl: 登录成功后的访问地址,注意:该方法只有当用户从登录页登录的时候才生效。如果用户没登陆,去访问一个需要登录后才能访问的页面,导致被跳转到登录页,此时从登录页登录进来就不生效了。
// successForwardUrl: 登录成功后的转发地址,全局生效,他就能解决上面那个问题。
// successForwardHandler: 登陆成功的处理逻辑,需要传一个Handler对象(这个方法允许开发人员使用自定义的 AuthenticationSuccessHandler 来处理用户成功登录后的行为,自定义类实现相关接口,重写方法。)
// failureUrl(String failureUrl): 指定登录失败后的跳转页面。默认为/login?error
// failureHandler(AuthenticationFailureHandler failureHandler): 指定自定义的登录失败处理器
// successForwardUrl(String successUrl): 指定登录成功后的跳转页面。默认为/login?error
// successHandler(AuthenticationSuccessHandler successHandler): 指定自定义的登录成功处理器
// and(): 用于连接其他配置方法
// permitAll(): 允许所有用户访问登录页面和登录处理URL
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")//指定登录访问路径(登录页面的form表单提交路径,注意必须一致,我的login.html里面写的提交路径是/login,所以这里我也写/login)
.defaultSuccessUrl("/index.html")
.failureUrl("/error.html");//指定用户名或密码错误访问的页面
}
}
再来看看现在6.x的写法:
/**
* 5版本只需@Configuration一个注解,不需要@EnableWebSecurity,
* 6需要同时引入,并且5是需要extends WebSecurityConfigurerAdapter类
*/
@EnableWebSecurity
@Configuration
public class SecurityConfiguration {
// 在springsecurity6.x中,推荐注册一个 SecurityFilterChain bean 的方式来实现,这更符合springboot基于组件开发的特点。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 6.x支持了lambda表达式写法
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
2.2 移除and
先来回顾5.x的写法:
// 5.x你可以使用and()方法,用于做连接其他配置方法, 你也可以不用。
// 用的写法如下:
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated() // authorizeRequests() 是Spring Security中用于配置请求权限的方法。
.and()
.formLogin().loginPage("/login.html") // formLogin() 是Spring Security中用于配置基于表单的身份验证的方法。
.and()
.csrf().disable() // csrf() 是Spring Security中用于处理csrf的方法。
}
// 不用的写法如下:
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated(); // authorizeRequests() 是Spring Security中用于配置请求权限的方法。
http.formLogin().loginPage("/login.html") // formLogin() 是Spring Security中用于配置基于表单的身份验证的方法。
http.csrf().disable() // csrf() 是Spring Security中用于处理csrf的方法。
}
再来看看现在6.x的写法:
// and()方法 已被移除! 当然你还可以5.x那种不用and的方法也可以,或者采用6.x推荐的lambda这种形式。
@Configuration
@EnableWebSecurity
public class MySecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(conf -> conf.requestMatchers("/login", "/info").permitAll().anyRequest().authenticated())
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf(withDefaults())
.cors(withDefaults()).build();
}
}
总而言之,言而总之。相较于5.x,6.x的改动。是对之前SpringBoot整合SpringSecurity的代码写法的一次较大改动。
3. 开始走进SpringSecurity6
3.1 获取SpringSecurity6
只要导入了依赖,其实就已经把SpringSecurity整合到你的springboot项目了。
此时,你可以试着点击一个请求,看看会发生什么。如果你没有凭证的情况下发起一个请求,会得到401的错误提示!
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
3.2 看看SpringBoot是怎么自动装配的SpringSecurity
怎么导入了一个依赖后,springsecurity就掌管了我们的springboot项目了呢?【这里因为springboot的自动配置功能实现的】

浙公网安备 33010602011771号