相关推荐recommended
Spring Security在6.0弃用WebSecurityConfigurationAdapter后该如何自定义配置介绍(新旧示例)
作者:mmseoamin日期:2023-12-21

前言:

在本人经历过新版和旧版的不同的配置折磨后,决心自己写一篇介绍造福大众,不好地方,欢迎伙指出ovo
本文针对Spring Security 6.0版本的自定义配置进行介绍,其中包含自定义数据源
UserDetailsService、自定义过滤链SecurityFilterChain等内容.

一、自定义过滤链SecurityFilterChain

1.旧版

在旧版的配置中,Security需要我们写一个类去继承他的WebSecurityConfigurerAdapter并把这个配置注入到容器中

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter{
	.....
}

在继承这个类后,我们可以在WebSecurityConfigurer里面去重写WebSecurityConfigurationAdapter类里面的一些方法来实现自定义过滤链等操作

Spring Security在6.0弃用WebSecurityConfigurationAdapter后该如何自定义配置介绍(新旧示例),Alt,第1张

实现自定义过滤链需要重写configure(HttpSecurity http)方法

	@Override
    protected void configure(HttpSecurity http) throws Exception {
    		http
    			.mvcMatchers("/login.html")
    			.permitAll()
    			.formLogin()
    			......
				.csrf()
				.disable();
    			
            }
这个方法的参数http采用的是链式设计,我们直接 ‘.’就可以进行设置。

特别的在旧版的配置中,我们需要手动将AuthenticationManager类暴露在全局中,使我们能在其他功能实现上可以拿到这个认证管理器

	// 暴露认证管理器给全局
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean()
            throws Exception {
        return super.authenticationManagerBean();
    }

旧版整体代码

@Configuration
// Security自定义配置器"
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
	@Override
    protected void configure(HttpSecurity http) throws Exception {
    		http
    			.mvcMatchers("/login.html")
    			.permitAll()
    			.formLogin()
    			......
				.csrf()
				.disable();
    			
            }
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean()
            throws Exception {
        return super.authenticationManagerBean();
    }
}

2.新版

在新版的Spring Security里面,如果我们继续继承WebSecurityConfigurationAdapter类的话,会出现如下弃用提示

public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter

在新版中我们只需要在自定义的配置类头顶添加一个 @EnableWebSecurity 注解即可将你写的这个注入到IOC容器中

该注解源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
		HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
	/**
	 * Controls debugging support for Spring Security. Default is false.
	 * @return if true, enables debug support with Spring Security
	 */
	boolean debug() default false;
}

从源码中我们可以看到内部以及实现了@Configuration注解,所有不需要我们手动注入IOC容器

在这个注解内部,作者也对新版的Spring Security配置SecurityFilterChain进行了特别说明

Spring Security在6.0弃用WebSecurityConfigurationAdapter后该如何自定义配置介绍(新旧示例),在这里插入图片描述,第2张

其中我们目前只需要注意securityFilterChain(HttpSecurity http)这个方法,这个方法就是我们配置自定义过滤链需要实现的方法

以下是实现伪代码

	@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
       		 http
                .authorizeHttpRequests((auth) -> {
                    auth
                            .mvcMatchers("/user/login")
                            .permitAll()
                            .anyRequest()
                            .authenticated();
                })
                .formLogin()
                ......
                .csrf()
                .disable()
             // 构建过滤链并返回
			return http.build();
     }

新版的实现方法不再和旧版一样在配置类里面重写方法,而是构建了一个过滤链对象并通过@Bean注解注入到IOC容器中

新版整体代码 (注意:新版AuthenticationManager认证管理器默认全局

@EnableWebSecurity
// Security自定义配置器
public class WebSecurityConfiger     {
@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
       		 http
                .authorizeHttpRequests((auth) -> {
                    auth
                            .mvcMatchers("/user/login")
                            .permitAll()
                            .anyRequest()
                            .authenticated();
                })
                .formLogin()
                ......
                .csrf()
                .disable()
             // 构建过滤链并返回
			return http.build();
     }
}

二、自定义数据源

在介绍如何配置前,我们先来看看底层是如何进行数据源认证的,下面是流程图:Spring Security在6.0弃用WebSecurityConfigurationAdapter后该如何自定义配置介绍(新旧示例),认证流程,第3张

在整个认证流程中,我们在提交用户表单数据的时候,会先被AbstractAuthenticationProcessingFilte捕获到如何被封装为UsernamePasswordAuthenticationToken 的token 对象,然后将整个对象交给AuthenticationManager认证管理器去实现认证,而这个时候就会去数据源UserDetailService类中去进行对应的认证方式,认证成功返回UserDetails对象,否则返回null,所以我们这个时候目的就比较明确了,为实现自定义数据源,我们就需要去重新实现一个类继承自UserDetailsSerive类,然后将这个类交给AuthenticationManager认证管理器,这样我们在认证的时候,认证管理器就会使用我们自定义的数据源

那么在新旧版本又是如何进行配置的,我们往下看

1.旧版

在旧版的配置中,我们首先去实现一个自定义实体类继承自UserDetails(注意:必须把这个类需要重写一些方法,这些方法都是给认证管理器去做一些工作的,这个User类还可以拥有一些其他的字段属性,比如说username,password…建议基于数据库中的User表来设置

@Data
//@ApiModel(value = "User对象", description = "用户对象")
public class User implements UserDetails {
	
	private String username;
	private String password;
	
	.....	
	
	@Override
    @JsonIgnore
    public Collection getAuthorities() {
        // 创建角色集合
        .........
        return grantedRoles;
    }
    @Override
    @JsonIgnore
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    @JsonIgnore
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    @JsonIgnore
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    @JsonIgnore
    public boolean isEnabled() {
        return true;
    }
}

然后是实现一个类继承UserDetailsService (注意我代码使用了mp查询数据,非mp正常在UserMapper里面写对应SQL,只要将最终查询到的用户User返回即可)

在这个类中,我们也使用了@Component注解将这个数据源交给IOC容器

@Component
// "登录数据源"
public class LoginUserDetailService implements UserDetailsService {
	// 数据库查询User信息
    private final UserMapper userMapper;
	private final RoleMapper RoleMapper
    @Autowired
    public LoginUserDetailService(UserMapper userMapper, RoleMapper roleMapper) {
        this.userMapper = userMapper;
        this.roleMapper = roleMapper;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户
        LambdaQueryWrapper queryWrapper_User = new LambdaQueryWrapper<>();
        queryWrapper_User.eq(User::getUsername,username);
        User user = userMapper.selectOne(queryWrapper_User);
        // 工具类判断是否查询user对象是否有效
        if (ObjectUtils.isEmpty(user)) {
            throw new UsernameNotFoundException("用户不存在");
        }
        LambdaQueryWrapper queryWrapper_Role = new LambdaQueryWrapper<>();
        queryWrapper_Role.eq(Role::getUid,user.getId());
        List roles = roleMapper.selectList(queryWrapper_Role);
        user.setRoles(roles);
        // User类继承了UserDetails,返回时会进行向上转型,所以我们将正常查询到的user数据装进User对象返回即可
        return user;
    }
}

现在到最后一步了,回到我们的WebSecurityConfigurationAdapter实现类里面,我们使用@Autowired注入我们刚刚写的LoginUserDetailService类使用 configure(AuthenticationManagerBuilder auth) 方法将这个实例交给AuthenticationManager,下面是具体代码

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
   @Autowired
   private LoginUserDetailService loginUserDetailService
   
   @Override
   protected void configure(HttpSecurity http) throws Exception {
   		http
   			.mvcMatchers("/login.html")
   			.permitAll()
   			.formLogin()
   			......
   			.csrf()
   			.disable();
   }
   
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   		auth.userDetailsService(loginUserDetailService);
   }
   
   @Override
   @Bean
   public AuthenticationManager authenticationManagerBean()
           throws Exception {
       	return super.authenticationManagerBean();
   }
}

2.新版

新版的比较简单,直接定义好数据源,注入就可以了,无需手动到配置类中去将它提交给AuthenticationManager进行管理。

@Component
public class LoginUserDetailService implements UserDetailsService {
    private final UserMapper userMapper;
    @Autowired
    public LoginUserDetailService(UserMapper userMapper, RoleMapper roleMapper) {
        this.userMapper = userMapper;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
      // 数据查询
      userMapper.方法
        
        return user;
    }
}

相比旧版,跳过了手动auth.userDetailsService(loginUserDetailService),新版底层会帮你执行这一步

=THE END=