前言相信大家在网上冲浪都遇到过登录时输入图片验证码的情况,既然我们已经学习了 Spring Security,也上手实现过几个案例,那不妨来研究一下如何实现这一功能 。
首先需要明确的是,登录时输入图片验证码,属于认证功能的一部分,所以本文不涉及授权功能 。
认证流程简析在上文中,我们介绍了认证流程,以及相关的关键类,可知 AuthenticationProvider 定义了 Spring Security 中的验证逻辑,该类的类关系图:
文章插图
【Spring Security自定义认证逻辑实现图片验证码登录】
我们来看下 AuthenticationProvider 的定义:
public interface AuthenticationProvider {Authentication authenticate(Authentication authentication) throws AuthenticationException;boolean supports(Class<?> authentication);}
可以看到,AuthenticationProvider 中就两个方法:- authenticate 方法用来做验证,就是验证用户身份 。
- supports 则用来判断当前的 AuthenticationProvider 是否支持对应的 Authentication 。
package org.springframework.security.core;public interface Authentication extends Principal, Serializable {// 获取用户的权限Collection<? extends GrantedAuthority> getAuthorities();//获取用户凭证,一般是密码,认证之后会移出,来保证安全性Object getCredentials(); //获取用户携带的详细信息,Web应用中一般是访问者的ip地址和sessionIdObject getDetails(); // 获取当前用户Object getPrincipal(); //判断当前用户是否认证成功boolean isAuthenticated();void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;}
官方文档里说过,当用户提交登录信息时,会将用户名和密码进行组合成一个实例UsernamePasswordAuthenticationToken,而这个类是 Authentication 的一个常用的实现类,用来进行用户名和密码的认证,类似的还有 RememberMeAuthenticationToken,它用于记住我功能 。
Spring Security 支持多种不同的认证方式,不同的认证方式对应不同的身份类型,每个 AuthenticationProvider 需要实现supports()方法来表明自己支持的认证方式,如我们使用表单方式认证,在提交请求时 Spring Security 会生成
UsernamePasswordAuthenticationToken,它是一个 Authentication,里面封装着用户提交的用户名、密码信息 。而对应的,哪个 AuthenticationProvider 来处理它?
我们在 DaoAuthenticationProvider 的基类
AbstractUserDetailsAuthenticationProvider 发现以下代码:
public boolean supports(Class<?> authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}
也就是说当web表单提交用户名密码时,Spring Security 由 DaoAuthenticationProvider 处理 。DaoAuthenticationProvider 的父类是
AbstractUserDetailsAuthenticationProvider,在该类中的 authenticate()方法用于处理认证逻辑,这里就不粘贴代码了,该方法大致逻辑如下:
- 首先实例化UserDetails对象,调用了retrieveUser方法获取到了一个user对象,retrieveUser是一个抽象方法 。该方法进一步会调用我们自己在登录时候的写的 loadUserByUsername 方法,具体在自定义的 UserDetailsService 或 InMemoryUserDetailsManager 等 。
- 如果没拿到信息就会抛出异常,如果查到了就会去调用preAuthenticationChecks的check(user)方法去进行预检查 。在预检查中进行了三个检查,因为UserDetail类中有四个布尔类型,去检查其中的三个,用户是否锁定、用户是否过期,用户是否可用 。
- 预检查之后紧接着去调用了additionalAuthenticationChecks方法去进行附加检查,这个方法也是一个抽象方法,检查密码是否匹配,在DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法中去具体实现,在里面进行了加密解密去校验当前的密码是否匹配 。我们想要校验图片验证码,就可以和密码一起校验,即我们重写 additionalAuthenticationChecks 方法 。
- 最后在 postAuthenticationChecks.check 方法中检查密码是否过期 。
- 所有的检查都通过,则认为用户认证是成功的 。用户认证成功之后,会将这些认证信息和user传递进去,调用createSuccessAuthentication方法 。
推荐阅读
- 什么是AOP,AOP能做什么?AOP的特点,Spring AOP的实现
- 聊聊 SpringBoot3 的 Micrometer Tracing
- SpringBoot中实现处理API接口敏感数据的脱敏
- 当我准备用SpringEvent优雅的解耦时,连续两个bug把我搞懵了
- 利用敬业签桌面便签软件实现钉钉添加自定义机器人定时提醒群成员
- 完全自定义实现SpringMVC核心组件
- 体验一下正式发布的SpringBoot3.0
- 微信上线超赞新玩法:可以自定义表情包!
- SpringBoot 使用 Feign 无废话 All-in-one 指南
- 二次封装 Spring Data JPA/MongoDB,打造更易用的数据访问层