首页
学习
活动
专区
圈层
工具
发布

SpringBoot升级到3.4.3,Spring Security从5.x升级到6.x,原来有这么多坑,不踩不知道,一踩一个准

SpringBoot升级到3.4.3,对应的Spring-Security也由5.x升级到6.x,在升级的过程中,坑是一踩一个准,变化非常大,现在就来整理一下这些变化。

一、被废除的类、注解和替代品

1、移除WebSecurityConfigurerAdapter

在Spring Security 5.x中,常用继承WebSecurityConfigurerAdapter类来进行安全配置。

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override   protected void configure(HttpSecurity http) throws Exception {       http           .authorizeRequests()               .antMatchers("/admin/**").hasRole("ADMIN")               .anyRequest().authenticated()           .and()           .formLogin();   }}

而在Spring Security 6.x中,推荐使用SecurityFilterChain和@EnableWebSecurity进行配置,移除了对WebSecurityConfigurerAdapter的依赖。

@Configuration@EnableWebSecuritypublic class SecurityConfig {

  @Bean   public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {       http           .authorizeHttpRequests()               .requestMatchers("/admin/**").hasRole("ADMIN")               .anyRequest().authenticated()           .and()           .formLogin();       return http.build();   }}

变化总结:WebSecurityConfigurerAdapter被移除,推荐使用SecurityFilterChain来配置HTTP安全设置。

2、AuthenticationManager的bean获取

在Spring Security 5.x中,AuthenticationManager的bean获取是通过继承 WebSecurityConfigurerAdapter 类,通过该类的authenticationManager() 获取bean。

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {   @Override   protected void configure(HttpSecurity http) throws Exception {       http           ....           // 增加登录拦截           .and().addFilter(new JWTLoginFilter(authenticationManager()))           // 增加是否登录过滤           .addFilter(new JWTAuthenticationFilter(authenticationManager()))           ...     }}

而在Spring Security 6.x中,WebSecurityConfigurerAdapter 被移除,推荐使用AuthenticationConfiguration 获取AuthenticationManager的bean。配置变得更加简洁,并且符合现代Spring风格。

@Configuration@EnableWebSecuritypublic class SecurityConfig {

  // 正确方式:通过AuthenticationConfiguration获取   @Bean   AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {       return authenticationConfiguration.getAuthenticationManager();   }}

变化总结:WebSecurityConfigurerAdapter被移除,转而通过AuthenticationConfiguration 来获取AuthenticationManager 的bean配置过来验证管理。

3、移除@EnableGlobalMethodSecurity

在Spring Security 5.x中,启用方法级安全控制使用@EnableGlobalMethodSecurity注解。

@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig {   // 允许使用@PreAuthorize等注解}

而在Spring Security 6.x中,@EnableGlobalMethodSecurity被新的注解@EnableMethodSecurity替代。这个新的注解提供了更细粒度的控制,简化了配置。

@Configuration@EnableMethodSecurity // 新的注解,用于开启方法级安全public class SecurityConfig {   // 允许使用@PreAuthorize等注解}

变化总结:@EnableGlobalMethodSecurity变成了@EnableMethodSecurity,新的注解会自动启用方法级别的安全,并且更加符合Spring 6.x的配置风格。

4、HttpSecurity的API变更

在Spring Security 5.x中,HttpSecurity配置较为常见的方式是使用http.authorizeRequests()来配置URL级别的权限控制。

http   .authorizeRequests()       .antMatchers("/admin/**").hasRole("ADMIN")       .anyRequest().authenticated()   .and()   .formLogin();

而在Spring Security 6.x中,authorizeRequests()方法已被替换为authorizeHttpRequests(),这是对方法命名的优化,旨在使API更加直观。

http   .authorizeHttpRequests()       .requestMatchers("/admin/**").hasRole("ADMIN")       .anyRequest().authenticated()   .and()   .formLogin();

不止这一个方法,还有其他方法,这里就不举例了。

变化总结:authorizeRequests()方法被authorizeHttpRequests()替代,主要是API方法的重命名,使得方法名称更加一致和简洁。

5、PermissionEvaluator实现类的配置

在使用Spring thymeleaf 整合 Spring Security 5.x 时,我需要在方法上加入自定义的授权,比如:

@PreAuthorize("hasPermission('goods', 'read') or hasRole('ROLE_ADMINISTRATOR')")

这个注解的意思是,只要具备 goods:read 或 ROLE_ADMINISTRATOR 权限的用户都可以访问这个方法。

在版本5.x,只要实现了PermissionEvaluator 接口,并注册为组件,就能实现这个功能。

@Component("permissionEvaluator")public class MyPermissionEvaluator implements PermissionEvaluator {   @Override   public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {       // 匿名访问时,无权限       if (authentication.getPrincipal().toString().equalsIgnoreCase("anonymousUser")) {           return false;       }       String privilege = targetDomainObject + ":" + permission;       return authentication.getAuthorities().stream().anyMatch(authority -> privilege.equalsIgnoreCase(authority.getAuthority()));   }

  /**    * 总是认为有权限返回true,否则返回false    */   @Override   public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {       return false;   }}

但在6.x中,我还需要在配置文件中配置一个bean才能实现同样的功能:

@BeanMethodSecurityExpressionHandler methodSecurityExpressionHandler(MyPermissionEvaluator permissionEvaluator) {   DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();   expressionHandler.setPermissionEvaluator(permissionEvaluator);   return expressionHandler;}

也就是说我们需要设置 ExpressionHandler 来关联这个Evaluator。不设置这个bean的话,框架一升级,所有的方法都没有权限访问,太尴尬了。

二、比较恶心的变化

1、退出的变化

在整合 jjwt 时,Spring Security 5.x 退出默认Get方法。

http   ....  // 退出登录的处理  .and().logout().addLogoutHandler(myLogoutHandler)

而在Spring Security 6.x中不一样,退出默认使用POST方式,GET方式需要自己调整。

http   ....  // 退出登录的处理 .logout(logout->logout       // 允许GET方式退出   .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))   .addLogoutHandler(myLogoutHandler)   // 清除安全上下文   .addLogoutHandler((request, response, authentication) -> {       SecurityContextHolder.clearContext();   })   .clearAuthentication(true)   )

这样也好,退出使用什么方法可以自定义。

需要注意的是,整合 jjwt 是前后端状态分离的架构设计。如果使用表单退出,默认的还是get方式。

2、登录调整的变化

在使用Spring thymeleaf 整合 Spring Security 5.x 时,登录后会以 get 方式直接跳往指定页面。

// 配置登录页面、登录成功跳转页面、登录失败跳转页面http.formLogin()       .loginPage("/login")       .successForwardUrl("/admin/index")       .failureUrl("/login?error=true")       .permitAll();

而在Spring Security 6.x中不一样,登录后的跳转方法默认使用POST方式,GET方式需要自己调整。

http   // 表单登录配置   .formLogin(form -> form       .usernameParameter("username")       .passwordParameter("password")       .loginPage("/login")      // .successForwardUrl("/admin/index") // 直接跳转页面,而且还是post方法       .defaultSuccessUrl("/admin/index", true) // 强制重定向,使用get方式,如果有历史页面也不会回到历史页面   //                .successHandler((request, response, authentication) -> {   //                    response.sendRedirect("/admin/index"); // 明确使用GET重定向   //                })       .failureUrl("/login?error=true")       .permitAll()   )

successForwardUrl()方法默认POST方法,如果需要使用get方式跳转,则需要使用 defaultSuccessUrl() 或 successHandler() 自定义get方式。

三、看似没变实则变化的

1、@EnableWebSecurity注解

Spring Security 5.x中,它长这样:

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value = { java.lang.annotation.ElementType.TYPE })@Documented@Import({ WebSecurityConfiguration.class,       SpringWebMvcImportSelector.class,       OAuth2ImportSelector.class })@EnableGlobalAuthentication@Configurationpublic @interface EnableWebSecurity {

  /**    * Controls debugging support for Spring Security. Default is false.    * @return if true, enables debug support with Spring Security    */   boolean debug() default false;}

Spring Security 6.x中,它长这样:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,       HttpSecurityConfiguration.class, ObservationImportSelector.class })@EnableGlobalAuthenticationpublic @interface EnableWebSecurity {

  /**    * Controls debugging support for Spring Security. Default is false.    * @return if true, enables debug support with Spring Security    */   boolean debug() default false;}

在新版本中,增加了HttpSecurityConfiguration.class, ObservationImportSelector.class两个类的导入。

以上这些都是根据我的实战经验进行整理的,可能还有其他变化,没有用到的用到了再说吧。

更多变化请看官网:https://docs.spring.io/spring-security/reference/index.html

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O5_koTkvhXv6eRuuk65QMGPw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券