1. 环境准备
SpringSecurity6
SpringSecurity认证有两种方式:
基于内存:
这种方式是在程序运行前,就将用户信息在配置类中进行配置,用户信息都是固定的,这种方式用的少。
基于数据库:
首先添加一个配置类:SecurityConfig
@Configuration 把该类放到Spring容器中
@EnableWebSecurity 开始Security.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private UmsSysUserDetailsService sysUserDetailsService;
/**
* 配置过滤器链,对login接口放行
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable());
// 放行login接口
http.authorizeHttpRequests(auth -> auth.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
/**
* AuthenticationManager:负责认证的
* DaoAuthenticationProvider:负责将 sysUserDetailsService、passwordEncoder融合起来送到AuthenticationManager中
* @param passwordEncoder
* @return
*/
@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
//
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(sysUserDetailsService);
// 关联使用的密码编码器
provider.setPasswordEncoder(passwordEncoder);
// 将provider放置进 AuthenticationManager 中,包含进去
ProviderManager providerManager = new ProviderManager(provider);
return providerManager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
编写Controller、Service、mapper和实体类。实体类需要实现 UserDetails,Service层需要实现UserDetailsService接口,实现loadUserByUsername方法。细节后面说。
2. 认证流程
Service层的login方法:
SpringSecurity中的认证要放到Authentication中,常用的实现类:UsernamePasswordAuthenticationToken 和 RememberMeAuthenticationToken,调用authenticationManager的authenticate方法,进行认证。
进入authenticate(ProviderManager.java)方法:
providerManager是AuthenticationManager的一个实现类,可以拿到providers,provider在哪里获取的呢???--------SecurityConfig中配置的。
/**
* AuthenticationManager:负责认证的
* DaoAuthenticationProvider:负责将 sysUserDetailsService、passwordEncoder融合起来送到AuthenticationManager中
* @param passwordEncoder
* @return
*/
@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
//
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(sysUserDetailsService);
// 关联使用的密码编码器
provider.setPasswordEncoder(passwordEncoder);
// 将provider放置进 AuthenticationManager 中,包含进去
ProviderManager providerManager = new ProviderManager(provider);
return providerManager;
}
DaoAuthenticationProvider 是基于数据库进行认证的。
继续下一步:
拿到provider调用authenticate方法,点进去是DaoAuthenticationProvider 的父类 AbstractUserDetailsAuthenticationProvider的一个方法。
这里有个断言,必须是UsernamePasswordAuthenticationToken才能够通过。
继续:
retrieveUser 到了这个方法,进去。
可以看到方法的返回值是:UserDetails,我们的实体类就是实现了这个接口,也就是说这个方法的结果就是加载目的用户信息。
继续往下:
这里的loadUserByUsername方法,就是我们需要自己写的逻辑代码。
这个Service可以和login方法所在的Service写在一起,但是要注意循环依赖的问题,这里分开写的。
跳进去验证一下:
在这个方法里面,你可以自定义实现逻辑代码,最后返回实现了UserDetails的实体类。
到了后面如果返回的UserDetails是null,则抛出异常,认证失败;不是null就封装AUthentication,并把认证状态改成true。
3. 流程图
4. 最后
代码来源:
最新SpringSecurity6.1.5-基于Vue3实现前后端分离动态权限、路由_哔哩哔哩_bilibili
如有错误,请指正!