Spring Security6.x认证模块核心——AuthenticationProvider解析
目录
前言
拳拳到肉,尽可能地通俗易懂。
一、如何快速入门?
Spring Security是基于过滤器进行设计的,它自身是一个SecurityFilterChain,在spring官网中,有很长的一个篇幅,对其架构进行介绍;而在这里,我推荐的入门方式是:可以将其快速地浏览一遍,心中只需要留下印象的是——Spring Security和过滤器链,你甚至直接可以将Spring Security看作是"一堆过滤器",这些过滤器组成了"过滤器链",而过滤器链中最重要的一个过滤器是:AuthorizationFilter。
二、准备环境
本演示使用的是:JDK 17 + Spring Security 6.2.1,maven依赖如下所示:
核心依赖,即红框框内所示。
Spring Security的最新版本是6.x,官网6.x演示搭配的是JDK 17,个人尝试了用JDK 8,运行项目时发现存在依赖问题,瞧了官网后便不再深究,直接切换JDK 17。虽然JDK 8是一个里程碑版本,但是JDK 17有替换8的可能。
三、入门说明
使用spring-initializer初始化一个spring boot项目,导入maven依赖,并添加下述两个源文件,启动项目进行初体验。
初体验目标:
1.理解SecurityConfig结构;
2.自定义Filter中需要加入什么逻辑;
3.SecurityConfig和自定义Filter如何搭配使用;
4.如何完成认证测试,认证成功和不成功分别是怎么样的;
四、入门步骤
1.SecurityConfig
代码如下(注释重要):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private AuthenticationManager authenticationManager;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
System.out.println(">>>>>>>>>> 进入securityFilterChain");
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login/**").permitAll()
.anyRequest().authenticated()
// 禁用csrf,公司中会使用Token方案,不太会用到csrf,禁用即可
).csrf(AbstractHttpConfigurer::disable)
// 在认证核心AuthorizationFilter执行之前,加入自定义Filter
// 自定义Filter需要完成的事情:装配Authentication对象,以便AuthorizationFilter执行时可以拿到
.addFilterBefore(new AuthenticationTokenFilter(authenticationManager), AuthorizationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder
) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
// 认证逻辑的提供者,设置UserDetailsService
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
// 认证逻辑的提供者,设置PasswordEncoder
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
authenticationManager = new ProviderManager(daoAuthenticationProvider);
return authenticationManager;
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("123")
.roles("USER")
.build();
// 使用内存模式的UserDetailsService
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public PasswordEncoder passwordEncoder() {
// 默认PasswordEncoder
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
2.自定义Filter
代码如下(示例):
public class AuthenticationTokenFilter extends OncePerRequestFilter {
public final AuthenticationManager manager;
public AuthenticationTokenFilter(AuthenticationManager manager) {
this.manager = manager;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("执行我的过滤器啦~~~");
String userName = request.getHeader("userName");
String password = request.getHeader("password");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, password);
// 验证Authentication对象,并将其设置在上下文环境中
SecurityContextHolder.getContext().setAuthentication(this.manager.authenticate(token));
filterChain.doFilter(request, response);
}
}
五、测试
访问请求+不带header信息;
访问请求+带header信息,header中包含正确的用户名/密码;
访问请求+带header信息,header中包含错误的用户名/密码;
总结
建议先看前两篇博文介绍,再看这一篇简单的实操文,能帮助深入理解。