文章目录

  • 目标
  • 自定义认证配置
    • 代码实践
    • 生成图片验证码部分
    • 验证“图片验证码”部分
      • 设置"图片验证码"参数
      • 自定义AuthenticationProvider
      • 核心配置
    • 注意

目标

  • 了解自定义认证方式集成图片验证码

    参考:Spring Security 实战 书籍

    使用redis时遇到的一些异常

    尽管创建了自己的实现,但仍创建了额外的DaoAuthenticationProvider

自定义认证配置

业务需求:用户进入登录页面,输入用户名,密码,验证码,进行登录。要求校验用户输入的验证码是否正确,不正确拒绝用户登陆。

思路: 依赖于上篇介绍的Spring Security 的认证流程,得知对于表单登陆,DaoAuthenticationProvider 是最终实现认证过程的Provider。我们期望将验证码的验证也加入其中,可以新建一个AuthenticationProvider继承DaoAuthenticationProvider 重写认证方法,用来实现集成图片验证码的功能。那么关于图片验证码的参数,就需要传递到这个自定义的Provider中,从上篇,依然可以得知 UsernamePasswordAuthenticationToken 可以在支持这个token类型的provider 中流转,从而获取参数。而且为了提供额外的参数支持,可以将额外参数设置到 details 参数中。为了方便设置这个details参数数据,还提供了 AuthenticationDetailsSource 和 WebAuthenticationDetails 供设置参数使用,我们实现接口加入设置图片验证码的参数即可。

代码实践

项目环境:spring boot 2.2.7 Jpa java8 mysql5.7

代码参考:https://github.com/gengzi/gengzi_spring_security 请切换到 captcha 分支

基本依赖,参考前面几篇文章,不在阐述。

由于前几篇已经对接了spring session 和 redisson 框架,就是要实现前后端分离的形式,也就不再依赖cookie 和 session 。所以下面的实现,就基于redis 来实现了。当然也会简单说下,使用cookie 和session 的实现方法。

生成图片验证码部分

可以参考:SpringSecurity实战(四)-集成图片验证码-过滤器方式实现

不再过多阐述。

验证“图片验证码”部分

依照上面的思路:我们需要自定义 AuthenticationProvider , AuthenticationDetailsSource 和WebAuthenticationDetails 。

WebAuthenticationDetails 作用:该类实现 Serializable 接口,主要用于设置 remoteAddress 和 sessionId 两个参数,供AuthenticationProvider 获取使用。

设置"图片验证码"参数

用户登陆请求会携带四个参数:

uuid: 434e1fba-edf9-4c58-87c0-bccc5b162fff   # redis存储验证码的key
username: admin   #用户名
password: 111      #密码
validCode: tk2zdf #输入的验证码

CaptchaWebAuthenticationDetails

代码参考:CaptchaWebAuthenticationDetails

根据用户输入的验证码信息,从redis 中获取正确的验证码值,进行对比。成功设置flag 参数为true,失败,false。


/**
* <h1>验证码Web身份验证详细信息</h1>
* <p>
* 扩展WebAuthenticationDetails 的参数,增加 flag 参数。
* <p>
* flag 参数用来判断 验证码是否正确的标识 true 正确,false 不正确
*
* @author gengzi
* @date 2020年11月27日15:34:38
*/
public class CaptchaWebAuthenticationDetails extends WebAuthenticationDetails {private RedisUtil redisUtil;// 验证码是否正确boolean flag = false;public CaptchaWebAuthenticationDetails(HttpServletRequest request, RedisUtil redisUtil) {super(request);this.setRedisUtil(redisUtil);validate(request);// 这里设置了 redisUtil 会导致序列化 springsecuritycontext 时,包含 redisUtil 这个类,这个类不能被序列化,所以用完就干掉this.setRedisUtil(null);}private void validate(HttpServletRequest request) {String uuid = request.getParameter("uuid");String validCode = request.getParameter("validCode");// 校验一下随机验证码String validCodeByRedis = (String) redisUtil.get(String.format(RedisKeyContants.VALIDCODEKEY, uuid));if (validCode.equals(validCodeByRedis)) {flag = true;redisUtil.del(String.format(RedisKeyContants.VALIDCODEKEY, uuid));}}public RedisUtil getRedisUtil() {return redisUtil;}public void setRedisUtil(RedisUtil redisUtil) {this.redisUtil = redisUtil;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}

特别注意:此类用到了redis,redis也会被当做一个参数进入到 provider 中,当认证完成,去序列化springsecuritycontext会出现一个报错,序列化失败(java.io.NotSerializableException: xxx)xxx代表就是序列化的类。所以不用了,就把它设置为null吧。

CaptchaWebAuthenticationDetailsSource

代码参考:CaptchaWebAuthenticationDetailsSource.java

/**
* <H1>验证码Web身份验证详细信息源</H1>
* <p>* 用于替换 UsernamePasswordAuthenticationFilter 中默认的  AuthenticationDetailsSource 属性
* 此类需要在核心配置中配置
* 代码示例:
* <p>
* protected void configure(HttpSecurity http) throws Exception {*    http...*        .formLogin()*        .authenticationDetailsSource(captchaWebAuthenticationDetailsSource)  // 替换原有的authenticationDetailsSource*        ....*        ;
* }
*
* @author gengzi
* @date 2020年11月27日15:43:06
*/
@Component
public class CaptchaWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {@Autowiredprivate RedisUtil redisUtil;@Overridepublic WebAuthenticationDetails buildDetails(HttpServletRequest context) {CaptchaWebAuthenticationDetails captchaWebAuthenticationDetails = new CaptchaWebAuthenticationDetails(context, redisUtil);return captchaWebAuthenticationDetails;}
}

自定义AuthenticationProvider

代码参考:CaptchaProvider.java

替换原有的DaoAuthenticationProvider 实现


/**
* <h1>验证码认证提供者</h1>
* <p>
* 重写 additionalAuthenticationChecks 方法,增加图片验证码的校验判断
* 成功,执行父类校验,走原有流程
* 失败,抛出异常,告知用户验证码错误
*
* 该类需要在核心配置中配置,以便于替换原有的 AuthenticationProvider
* 代码示例:*       @Autowired*       private AuthenticationProvider authenticationProvider;*          protected void configure(AuthenticationManagerBuilder auth) throws Exception {*         // 设置 userDetailsService 和  authenticationProvider 都会创建一个 Provider。 如果仅需要一个,请只设置一个*         auth.authenticationProvider(authenticationProvider);*     }
*
*
*
* @author gengzi
* @date 2020年11月26日13:51:32
*/
@Slf4j
@Component
public class CaptchaProvider extends DaoAuthenticationProvider {/*** 使用构造方法,将 userDetailsService 和 encoder 注入** @param userDetailsService 用户详细服务* @param encoder            密码加密方式*/public CaptchaProvider(UserDetailsService userDetailsService, PasswordEncoder encoder) {this.setUserDetailsService(userDetailsService);this.setPasswordEncoder(encoder);}/*** 验证码验证是否正确** @param userDetails* @param authentication* @throws AuthenticationException*/@Overrideprotected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {// 获取验证码是否通过的标识CaptchaWebAuthenticationDetails details = (CaptchaWebAuthenticationDetails) authentication.getDetails();boolean flag = details.isFlag();if (!flag) {// 不成功 ,抛出异常throw new CaptchaErrorException(RspCodeEnum.ERROR_VALIDCODE.getDesc());}// 成功,调用父类验证super.additionalAuthenticationChecks(userDetails, authentication);}
}

这里还提供了一个 CaptchaErrorException ,主要用于返回 认证异常,这样可以走认证失败的事件。

代码参考:CaptchaErrorException.java

/**
* <h1>验证码错误异常</h1>
*
* @author gengzi
* @date 2020年11月26日13:49:19
*/
public class CaptchaErrorException extends AuthenticationException {public CaptchaErrorException(String msg) {super("验证码输入错误");}
}

核心配置

代码参考:WebSecurityConfig.java

太长了,只粘重要部分。

 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CaptchaWebAuthenticationDetailsSource captchaWebAuthenticationDetailsSource;@Autowiredprivate AuthenticationProvider authenticationProvider;/*** 认证管理器配置方法*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//        auth.userDetailsService(userDetailsService);
//        // 增加认证提供者// 设置 userDetailsService 和  authenticationProvider 都会创建一个 Provider。 如果仅需要一个,请只设置一个auth.authenticationProvider(authenticationProvider);}@Overrideprotected void configure(HttpSecurity http) throws Exception {// 自定义表单认证方式http.apply(otherSysOauth2LoginAuthenticationSecurityConfig).and().authorizeRequests()// 放行swagger-ui相关的路径.antMatchers(IgnoringUrlConstant.IGNORING_URLS).permitAll().antMatchers(IgnoringUrlConstant.IGNORING_STATIC_URLS).permitAll().antMatchers(IgnoringUrlConstant.OAUTH2_URLS).permitAll().antMatchers("/getLoginCode").permitAll().antMatchers("/codeBuildNew/**").permitAll()  // 都可以访问.anyRequest().authenticated().and().formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll().and().csrf().disable()// csrf 防止跨站脚本攻击.formLogin()// ----------------------------- start -------------------------------.authenticationDetailsSource(captchaWebAuthenticationDetailsSource)  // 替换原有的authenticationDetailsSource// ---------------------------- end  -----------------------------------.successHandler(userAuthenticationSuccessHandler).failureHandler(userAuthenticationFailureHandler).and().sessionManagement((sessionManagement) -> sessionManagement.maximumSessions(100).sessionRegistry(sessionRegistry()));}}

注意

在配置 authenticationProvider 时, 出现了一个错误。

我进行了如下的配置:

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {// 设置了 userDetailsServiceauth.userDetailsService(userDetailsService);// 设置了认证提供auth.authenticationProvider(authenticationProvider);}

我的预期是执行认证过程,CaptchaProvider 会替换原有的DaoAuthenticationProvider 认证,也就是仅有一个认证Provider。但是出现了两个 Provider,导致用户验证码输入错误,CaptchaProvider 认证不通过,但是DaoAuthenticationProvider 认证就通过了,因为它不会校验验证码是否正确。导致用户在输入错误验证码的时候,依然可以正确登陆。查阅github 上的问题后,得知设置 userDetailsService 创建一个 Provider,再设置一个 authenticationProvider 会再创建一个 Provider。

所以,解决办法很简单,删除掉userDetailsService 的配置,因为不在这里配置userDetailsService ,我也在 Provider 中进行了注入。

问题原因: 尽管创建了自己的实现,但仍创建了额外的DaoAuthenticationProvider

听说点赞关注的人,身体健康,一夜暴富,升职加薪迎娶白富美!!!

点我领取每日福利
微信公众号:耿子blog
GitHub地址:gengzi

SpringSecurity实战(六)-集成图形验证码-自定义认证实现相关推荐

  1. web前端入门到实战:实现图形验证码

    什么是图形验证码 图形验证码是验证码的一种.验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers a ...

  2. gin集成图形验证码

    简介 github 文档 生成图形验证码 package mainimport ("github.com/gin-gonic/gin""github.com/mojocn ...

  3. Spring Security 图形验证码

    图形验证码 自定义过滤器 自定义过滤器 相关代码如下 获取图片验证码 @Controller public class CaptchaController {@Autowiredprivate Pro ...

  4. 【.NET Core项目实战-统一认证平台】第六章 网关篇-自定义客户端授权

    上篇文章[.NET Core项目实战-统一认证平台]第五章 网关篇-自定义缓存Redis 我们介绍了网关使用Redis进行缓存,并介绍了如何进行缓存实现,缓存信息清理接口的使用.本篇我们将介绍如何实现 ...

  5. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

  6. SpringSecurity添加图形验证码认证功能

    SpringSecurity添加图形验证码认证功能 第一步:图形验证码接口 1.使用第三方的验证码生成工具Kaptcha https://github.com/penggle/kaptcha @Con ...

  7. SpringSecurity实战:基于mysql自定义SpringSecurity权限认证规则

    上文<Spring Security 源码分析:Spring Security 授权过程>已经详细分析了Spring Security 授权过程,接下来通过上文的授权过程我们可以自定义授权 ...

  8. SpringSecurity权限管理系统实战—六、SpringSecurity整合JWT

    文章目录 系列目录 前言 一.无状态登录 二.JWT介绍 1.什么是jwt 头部(Header) 载荷(Payload) 签名(Signature) 2.JWT工作流程 3.简单实现 三.整合JWT ...

  9. 【Spring Boot组件集成实战】集成Kaptcha谷歌验证码

    更多精彩内容,请访问 Spring Boot组件集成实战专栏 ! 推荐项目:一套基于Spring Boot+Layui的内容管理系统/快速开发脚手架(含完整的开发文档.演示网址等) 文章目录 1. 验 ...

  10. 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...

最新文章

  1. 现代简明魔法 php内核,你所必须知道的PHP 9种非常有用的函数和特征!
  2. inux 后台执行命令
  3. python opencv图像处理程序_Python-OpenCV学习(四):基本图像处理
  4. 超全局变量数组get post requerst resver的使用规则
  5. leetcode 1310. 子数组异或查询(位运算)
  6. idea中新建javaWeb项目
  7. matlab字符串操作总结
  8. laravel无法运行php,Laravel:php artisan服务无法启动
  9. lpc2000 filash utility 程序烧写工具_重点必看 | 取证小程序开发之第四届美亚杯硬盘信息快速解题...
  10. 学习总结 java 创建及其练习
  11. FrameWork数据权限浅析4之基于多维度配置表实现行级数据安全
  12. 拓端tecdat|R语言多分类logistic逻辑回归模型在混合分布模拟个人风险损失值评估的应用
  13. 基于SSM的电脑商城(源码)
  14. 灵悟礼品网上专卖店——画出E-R图
  15. cad.net 筛选、选择集
  16. 论文参考文献格式及意义
  17. IOS 隐藏app图标
  18. 天行健,君子以自强不息;地势坤,君子以厚德载物的权威解释
  19. [ubuntn]常用软件安装方法
  20. iOS - 找出汉字拼音首字母

热门文章

  1. Error: for..in loops iterate over the entire prototype chain
  2. C# winform 右下角弹窗
  3. 计算机安全模式无法启动,电脑安全模式启动不了怎么办
  4. 【error】RuntimeError: size mismatch
  5. mysql正则时间格式_用正则表达式校验时间格式的正确性
  6. opencv学习日记
  7. 批量下载Landsat快视图,为批量下载Landsat Level1数据做准备
  8. 【xsy1061】排列 树状数组
  9. 一个UE4崩溃问题以及解决方案
  10. 20162327WJH第二次实验——树