当前位置:   article > 正文

SpringSecurity重写DaoAuthenticationProvider,自定义密码对比类

重写daoauthenticationprovider
  1. 需求,默认的SpringSecurity密码校验,无法满足需求,需要自定义来进行密码比对

a、网上找了一些教程,没法一步到位,很多代码,连import都没有贴出来,烦死了。

b、这里说明一下,我是为了图方便,系统原有密码登录情况下,要加入验证码登录,由于验证码时4-5位纯数字,而密码是6位以上的数字+字母,因此不想Filter这些全部来搞,因此想了这个办法简洁一点,也很快能够完成功能。

c、主要涉及两个地方,1是新增MyAuthenticationProvider类,继承DaoAuthenticationProvider,2,public class SecurityConfig extends WebSecurityConfigurerAdapter配置类中修改protected void configure(AuthenticationManagerBuilder auth) throws Exception方法。

MyAuthenticationProvider.java代码如下:

  1. import com.dhproject.common.constant.CacheConstants;
  2. import com.dhproject.common.core.redis.RedisCache;
  3. import com.dhproject.common.exception.ServiceException;
  4. import com.dhproject.common.utils.SecurityUtils;
  5. import com.dhproject.common.utils.StringUtils;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.security.authentication.BadCredentialsException;
  8. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  9. import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
  10. import org.springframework.security.core.AuthenticationException;
  11. import org.springframework.security.core.userdetails.UserDetails;
  12. import org.springframework.security.core.userdetails.UserDetailsService;
  13. import org.springframework.security.crypto.password.PasswordEncoder;
  14. import javax.annotation.Resource;
  15. public class MyAuthenticationProvider extends DaoAuthenticationProvider {
  16. //passwordEncoder密码比较器
  17. @Resource
  18. private PasswordEncoder passwordEncoder;
  19. //Redis
  20. @Autowired
  21. protected RedisCache redisCache;
  22. /**
  23. * 构造初始化
  24. * @param userDetailsService
  25. * @param passwordEncoder
  26. * @param redisCache
  27. */
  28. public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, RedisCache redisCache) {
  29. super();
  30. // 这个地方一定要对userDetailsService赋值,不然userDetailsService是null (这个坑有点深)
  31. setUserDetailsService(userDetailsService);
  32. //passwordEncoder由于父类是private,这里需要自定义初始化后才能使用
  33. this.passwordEncoder = passwordEncoder;
  34. //这个是Redis,根据实际情况初始化
  35. this.redisCache = redisCache;
  36. }
  37. /**
  38. * 重写该方法
  39. * @param userDetails
  40. * @param authentication
  41. * @throws AuthenticationException
  42. */
  43. @Override
  44. protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  45. if (authentication.getCredentials() == null) {
  46. this.logger.debug("Failed to authenticate since no credentials provided");
  47. throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
  48. } else {
  49. String mobile = authentication.getPrincipal().toString();
  50. String password = authentication.getCredentials().toString();
  51. //这里进行密码和验证码验证
  52. //如果是四位纯数字,那么认定是验证码登录
  53. if(isCodeLogin(password)){
  54. // 1. 检验Redis手机号的验证码
  55. String verifyKey = CacheConstants.SMS_CODE_KEY+mobile;
  56. String code = redisCache.getCacheObject(verifyKey);
  57. if (StringUtils.isEmpty(code)) {
  58. throw new ServiceException("验证码已经过期或尚未发送,请重新发送验证码");
  59. }
  60. if (!password.equals(code)) {
  61. throw new ServiceException("输入的验证码不正确,请重新输入");
  62. }
  63. // 2. 短信验证成功后要删除redis中的验证码
  64. redisCache.deleteObject(verifyKey);
  65. }else{
  66. if (!this.passwordEncoder.matches(password, userDetails.getPassword())) {
  67. this.logger.debug("Failed to authenticate since password does not match stored value");
  68. throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
  69. }
  70. }
  71. }
  72. }
  73. /**
  74. * 时都是验证码登录
  75. * @param password
  76. * @return
  77. */
  78. public boolean isCodeLogin(String password) {
  79. return StringUtils.isNumeric(password) && (password.length()==4||password.length()==5);
  80. }
  81. }

SecurityConfig.java代码如下:一定要注意看说明,不然肯定最后还会有问题

  1. /**
  2. * 身份认证接口
  3. */
  4. @Override
  5. protected void configure(AuthenticationManagerBuilder auth) throws Exception
  6. {
  7. //定义了新的Provider方法,就不能使用默认的userDetailsService进行构造,否则抛出BadCredentialsException异常,代码会继续执行
  8. auth.authenticationProvider(new MyAuthenticationProvider(userDetailsService,bCryptPasswordEncoder(),redisCache));
  9. //默认的userDetailsService进行构造
  10. //auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
  11. }

SecurityConfig.java类,全部文件,我也贴一下,方便各位参考:

  1. import com.dhproject.common.core.redis.RedisCache;
  2. import com.dhproject.framework.security.sms.MyAuthenticationProvider;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.http.HttpMethod;
  6. import org.springframework.security.authentication.AuthenticationManager;
  7. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  8. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  9. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11. import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
  12. import org.springframework.security.config.http.SessionCreationPolicy;
  13. import org.springframework.security.core.userdetails.UserDetailsService;
  14. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  15. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  16. import org.springframework.security.web.authentication.logout.LogoutFilter;
  17. import org.springframework.web.filter.CorsFilter;
  18. import com.dhproject.framework.config.properties.PermitAllUrlProperties;
  19. import com.dhproject.framework.security.filter.JwtAuthenticationTokenFilter;
  20. import com.dhproject.framework.security.handle.AuthenticationEntryPointImpl;
  21. import com.dhproject.framework.security.handle.LogoutSuccessHandlerImpl;
  22. /**
  23. * spring security配置
  24. *
  25. * @author
  26. */
  27. @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
  28. public class SecurityConfig extends WebSecurityConfigurerAdapter
  29. {
  30. @Autowired
  31. protected RedisCache redisCache;
  32. /**
  33. * 自定义用户认证逻辑
  34. */
  35. @Autowired
  36. private UserDetailsService userDetailsService;
  37. /**
  38. * 认证失败处理类
  39. */
  40. @Autowired
  41. private AuthenticationEntryPointImpl unauthorizedHandler;
  42. /**
  43. * 退出处理类
  44. */
  45. @Autowired
  46. private LogoutSuccessHandlerImpl logoutSuccessHandler;
  47. /**
  48. * token认证过滤器
  49. */
  50. @Autowired
  51. private JwtAuthenticationTokenFilter authenticationTokenFilter;
  52. /**
  53. * 跨域过滤器
  54. */
  55. @Autowired
  56. private CorsFilter corsFilter;
  57. /**
  58. * 允许匿名访问的地址
  59. */
  60. @Autowired
  61. private PermitAllUrlProperties permitAllUrl;
  62. /**
  63. * 解决 无法直接注入 AuthenticationManager
  64. *
  65. * @return
  66. * @throws Exception
  67. */
  68. @Bean
  69. @Override
  70. public AuthenticationManager authenticationManagerBean() throws Exception
  71. {
  72. return super.authenticationManagerBean();
  73. }
  74. /**
  75. * anyRequest | 匹配所有请求路径
  76. * access | SpringEl表达式结果为true时可以访问
  77. * anonymous | 匿名可以访问
  78. * denyAll | 用户不能访问
  79. * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
  80. * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
  81. * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
  82. * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
  83. * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
  84. * hasRole | 如果有参数,参数表示角色,则其角色可以访问
  85. * permitAll | 用户可以任意访问
  86. * rememberMe | 允许通过remember-me登录的用户访问
  87. * authenticated | 用户登录后可访问
  88. */
  89. @Override
  90. protected void configure(HttpSecurity httpSecurity) throws Exception
  91. {
  92. // 注解标记允许匿名访问的url
  93. ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
  94. permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
  95. httpSecurity
  96. // CSRF禁用,因为不使用session
  97. .csrf().disable()
  98. // 禁用HTTP响应标头
  99. .headers().cacheControl().disable().and()
  100. // 认证失败处理类
  101. .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
  102. // 基于token,所以不需要session
  103. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
  104. // 过滤请求
  105. .authorizeRequests()
  106. // 对于登录login 注册register 验证码captchaImage 允许匿名访问
  107. .antMatchers("/login", "/register", "/captchaImage", "/container/public/sendSms").permitAll()
  108. // 静态资源,可匿名访问
  109. .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**", "/public/**").permitAll()
  110. .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
  111. // 除上面外的所有请求全部需要鉴权认证
  112. .anyRequest().authenticated()
  113. .and()
  114. .headers().frameOptions().disable();
  115. // 添加Logout filter
  116. httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
  117. // 添加JWT filter
  118. httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  119. // 添加CORS filter
  120. httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
  121. httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
  122. }
  123. /**
  124. * 强散列哈希加密实现
  125. */
  126. @Bean
  127. public BCryptPasswordEncoder bCryptPasswordEncoder()
  128. {
  129. return new BCryptPasswordEncoder();
  130. }
  131. /**
  132. * 身份认证接口
  133. */
  134. @Override
  135. protected void configure(AuthenticationManagerBuilder auth) throws Exception
  136. {
  137. //定义了新的Provider方法,就不能使用默认的userDetailsService进行构造,否则抛出BadCredentialsException异常,代码会继续执行
  138. auth.authenticationProvider(new MyAuthenticationProvider(userDetailsService,bCryptPasswordEncoder(),redisCache));
  139. //默认的userDetailsService进行构造
  140. //auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
  141. }
  142. }

2.可能存在问题问题

MyDaoAuthenticationProvider重写DaoAuthenticationProvider中additionalAuthenticationChecks方法后,自定义验证密码错误后,抛出BadCredentialsException异常,代码会继续执行,会执行DaoAuthenticationProvider中的additionalAuthenticationChecks发放,导致自定义抛出的BadCredentialsException的异常无用。

代码注释中已经说明

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/370528
推荐阅读
相关标签
  

闽ICP备14008679号