Authentication
AuthenticationManager : 认证管理器
实现类:
- ProviderManager
AuthenticationProvider :
实现类:
- DaoAuthenticationProvider
- RememberMeAuthenticationProvider
方法:
- authenticate()
- supports() : 判断当前AuthenticationProvider是否支持对应的身份类型
AuthenticationProvider
AbstractUserDetailsAuthenticationProvider :
具体的认证逻辑在AbstractUserDetailsAuthenticationProvider#authenticate 方法中
DaoAuthenticationProvider
继承自 AbstractUderDetailsAuthenticationProvider
总结:
AbstractUserDetailsAuthenticationProvider#authenticate :
- 先从缓存里面获取
- 获取不到 retrieveUser 找回user,,也就是从UserDetailsService#loadUserByUsername中找UserDetails
- 找到之后对 UserDetails进行判断
preAuthenticationChecks : 检查用户状态
additionalAuthenticationChecks : 密码的校验
postAuthenticationChecks : 密码是否过期- 创建一个认证后的UsernamePasswordAuthenticationToken
ProviderManager
ProviderManager本身可以有多个,,也就是不同的认证方式,,多个ProviderManager可以共用一个parent,,
共有的认证方式可以在parent中处理
ProviderManager#authenticate 方法,,会挨个遍历AuthenticationProvider,,调用AuthenticationProvider#authenticate,
AbstractAuthenticationProcessingFilter
如果使用用户名密码登录: 实现类: UsernamePasswordAuthenticationFilter
过滤器会构造出一个 Authentication
对象,则是UsernamePasswordAuthenticationToken
,然后在ProviderManager中进行认证,,认证成功进入成功回调
配置多个数据源
@Configuration
public class SecurityConfig01 extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService us1(){
/**
* AuthenticationProvider#retrieveUser 获取user 会调用 UserDetailsService#loadUserByUsername
*/
// 配置UserDetailsService 返回UserDetails
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager(User.builder().username("cc").password("{noop}123").roles("admin").build());
return userDetailsService;
}
@Bean
UserDetailsService us2(){
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager(User.builder().username("hh").password("{noop}123").roles("user").build());
return userDetailsService;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
DaoAuthenticationProvider provider01 = new DaoAuthenticationProvider();
provider01.setUserDetailsService(us1());
DaoAuthenticationProvider provider02 = new DaoAuthenticationProvider();
provider02.setUserDetailsService(us2());
ProviderManager providerManager = new ProviderManager(provider01,provider02);
return providerManager;
}
}
加入登录验证码
- 自定义认证逻辑 添加验证码
就是添加自己的AuthenticationProvider,,,在原有的 DaoAuthenticationProvider
基础上修改,,添加验证码校验:
- 生成验证码,并存入session中
导包:
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
@Configuration
public class CaptchaConfig {
@Bean
public Producer kaptcha(){
/**
* DefaultKaptcha implements Producer
* */
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.image.width","150");
properties.setProperty("kaptcha.image.height","50");
properties.setProperty("kaptcha.textproducer.char.string","0123456789");
properties.setProperty("kaptcha.textproducer.char.length","4");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
生成图片的接口:
@Autowired
Producer producer;
@GetMapping("/verifyCode")
public void verifyCode(HttpServletResponse resp, HttpSession session) throws IOException {
String text = producer.createText();
System.out.println("text = " + text);
BufferedImage image = producer.createImage(text);
session.setAttribute("captcha",text);
// 返回图片,要写contentType
resp.setContentType("image/jpeg");
try(ServletOutputStream out = resp.getOutputStream()){
ImageIO.write(image,"jpg",out);
}
}
- 自定义AuthencationProvider ,比较验证码
public class CaptchaAuthenticationProvider extends DaoAuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = requestAttributes.getRequest();
String captcha = req.getParameter("captcha");
String sessionCaptcha = (String) req.getSession().getAttribute("captcha");
if (captcha != null && captcha.equals(sessionCaptcha)){
return super.authenticate(authentication);
}
throw new AuthenticationServiceException("验证码错误");
}
}
- 在配置文件中引入自己的AuthenticationProvider,放开验证码接口
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService us1(){
return new InMemoryUserDetailsManager(User.withUsername("cc").password("{noop}123").roles("admin").build());
}
@Bean
AuthenticationProvider captchaAuthenticationProvider(){
CaptchaAuthenticationProvider provider = new CaptchaAuthenticationProvider();
provider.setUserDetailsService(us1());
return provider;
}
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
ProviderManager providerManager = new ProviderManager(captchaAuthenticationProvider());
return providerManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/verifyCode").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
}