本文主要介绍了通过SpringSceurity实现短信验证码登录。本文通过示例代码为您做了非常详细的介绍,对您的学习或工作有一定的参考价值。有需要的朋友可以参考一下。
目录
一、短信登录验证机制原理分析1。账户密码登录过程2。短信验证码登录流程2。代码实现3。测试1。获取验证码2。登录。
一、短信登录验证机制原理分析
在了解短信验证码的登录机制之前,首先要了解用户账号密码登录的机制。下面简单分析一下Spring Security是如何验证基于用户名和密码的登录方式的。
分析完了,我们再来思考如何将短信登录验证整合到Spring安全中。
1、账号密码登陆的流程一般账号密码的登录都有图形验证码和记住我的功能,所以其一般流程如下。
1.用户输入用户名、账号、图片验证码后点击登录。然后,对于springSceurity,您将首先进入SMS验证码过滤器,因为它将在
在userpasswordtauthenticationfilter之前,使用会话中存在的图片验证码的验证码检查当前验证码的信息。
2.短信验证码通过后,进入usernamepasswordtauthenticationfilter,根据输入的用户名和密码信息,构造一个暂时未认证的。
usernamepasswordtauthenticationtoken,并将usernamepasswordtauthenticationtoken交给AuthenticationManager进行处理。
3.AuthenticationManager本身不进行身份验证处理。他通过for-each遍历找到一个与当前登录模式匹配的AuthenticationProvider,并将其用于身份验证处理。
对于用户名和密码的登录方法,这个提供者是DaoAuthenticationProvider。
4.在该提供程序中执行一系列验证处理。如果验证通过,将重新构造一个带有身份验证的usernamepasswordtauthenticationtoken,并且这个
将令牌传递回usernamepasswordtauthenticationfilter。
5.在这个过滤器的父类AbstractAuthenticationProcessing Filter中,会根据上一步的验证结果跳转到successHandler或failureHandler。
流程图
2、短信验证码登陆流程因为短信登录没有集成到Spring Security中,所以我们经常需要自己开发短信登录逻辑,集成到Spring Security中,所以这里我们模仿账号。
密码登录实现短信验证码登录。
1.有一个用于用户名和密码登录的usernamepasswordtauthenticationfilter。让我们制作一个SmsAuthenticationFilter,并粘贴代码来更改它。
2.用户名和密码登录需要usernamepasswordtauthenticationtoken。让我们创建一个SmsAuthenticationToken,并粘贴代码来更改它。
3.用户名和密码登录需要DaoAuthenticationProvider。我们模仿它,它也是implements authenticationprovider,叫做SMSauthenationProvider。
这张图是我在网上找到的,不想画了。
我们自己做好以上三个类之后,想要达到的效果如上图所示。当我们使用短信验证码登录时:
1.首先通过SmsAuthenticationFilter,构造一个不需要认证的SmsAuthenticationToken,然后交给AuthenticationManager进行处理。
2.AuthenticationManager通过for-each选择合适的提供者进行处理。当然,我们希望这个提供者是SmsAuthenticationProvider。
3.验证通过后,重新构造一个经过身份验证的SmsAuthenticationToken,并将其返回给SmsAuthenticationFilter。
过滤器根据上一步的验证结果跳转到成功或失败处理逻辑。
二、代码实现
1、SmsAuthenticationToken
首先我们写SmsAuthenticationToken。在这里,我们直接引用usernamepasswordtauthenticationtoken源代码,直接粘贴,修改。
解释
Principal最初代表用户名,此处保留,但仅代表手机号码。
凭据原码密码,短信登录不能用,直接删除。
SmsCodeAuthenticationToken()的构造方法有两种:一种是构造未认证令牌,另一种是构造认证令牌。
剩下的几个方法去除无用属性即可。
代码
公共类SmsCodeAuthenticationToken扩展了AbstractAuthenticationToken
private static final long serial version uid=springsecuritycorporation .串行版本UID
/**
* 在usernamepasswordtuthenticationtoken中该字段代表登录的用户名,
* 在这里就代表登录的手机号码
*/
私有最终对象主体;
/**
* 构建一个没有鉴权的SmsCodeAuthenticationToken
*/
公共SmsCodeAuthenticationToken(对象主体){
超级(空);
this.principal=本金
set authenticated(false);
}
/**
* 构建拥有鉴权的SmsCodeAuthenticationToken
*/
公共SmsCodeAuthenticationToken(对象主体,集合?扩展授予的权限权限){
超级(权威);
this.principal=本金
//必须使用超级,因为我们重写了
超级棒。set authenticated(真);
}
@覆盖
公共对象getCredentials() {
返回空
}
@覆盖
公共对象getPrincipal() {
返回这个校长
}
@覆盖
公共空集已验证(布尔值已验证)引发IllegalArgumentException {
if (isAuthenticated) {
抛出新的IllegalArgumentException(
无法将此标记设置为接受授予权限列表的可信使用构造函数);
}
超级棒。set authenticated(false);
}
@覆盖
public void eraseCredentials() {
超级棒。erasecredentials();
}
}
2、SmsAuthenticationFilter
然后编写SmsAuthenticationFilter,参考usernamepasswordtuthenticationfilter的源码,直接粘过来,改一改。
说明
原本的静态字段有用户名和密码,都干掉,换成我们的手机号字段。
SmsCodeAuthenticationFilter()中指定了这个过滤器的拦截Url,我指定为邮政方式的/短信/登录。
剩下来的方法把无效的删删改改就好了。
代码
公共类SmsCodeAuthenticationFilter扩展了抽象认证处理过滤器{
/**
*表格表单中手机号码的字段名字
*/
公共静态最终字符串SPRING _ SECURITY _ FORM _ MOBILE _ KEY= MOBILE ;
私有字符串mobileParameter=移动
/**
* 是否仅邮政方式
*/
私有布尔postOnly=true
public SmsCodeAuthenticationFilter(){
//短信验证码的地址为/短信/登录请求也是邮政
super(new AntPathRequestMatcher(/SMS/log in , POST ));
}
@覆盖
公共身份验证尝试身份验证(HttpServletRequest请求,HttpServletResponse响应)引发认证异常{
if (postOnly!request.getMethod().等于( POST ){
引发新的AuthenticationServiceException(
不支持身份验证方法:“请求。get method()";
}
String mobile=obtainMobile(请求);
if (mobile==null) {
手机="";
}
手机=移动。trim();
SmsCodeAuthenticationToken authRequest=new SmsCodeAuthenticationToken(mobile);
//允许子类设置"详细信息"属性
setDetails(请求,授权请求);
返回this.getAuthenticationManager().身份验证(authRequest);
}
受保护的字符串获取移动(HttpServletRequest请求){
退货请求。getparameter(移动参数);
}
受保护的void set详细信息(http servlet请求请求,SmsCodeAuthenticationToken authRequest){
authrequest。设置详细信息(authenticationdetailssource。构建细节(请求));
}
公共字符串getMobileParameter() {
返回移动参数
}
public void setmobile参数(字符串移动参数){
Assert.hasText(mobileParameter,移动参数不得为空或null’);
这个。移动参数=移动参数;
}
public void setpost only(仅布尔型post
这个。仅发布=仅发布;
}
}
3、SmsAuthenticationProvider
这个方法比较重要,这个方法首先能够在使用短信验证码登陆时候被AuthenticationManager挑中,其次要在这个类中处理验证逻辑。
说明
实现认证提供者接口,实现认证()和支架()方法。
代码
公共类SmsCodeAuthenticationProvider实现AuthenticationProvider {
私有userdailsservice用户详细信息服务;
/**
* 处理会议工具类
*/
私有会话策略session strategy=new http session session strategy();
string SESSION _ KEY _ PREFIX= SESSION _ KEY _ FOR _ CODE _ SMS ;
@覆盖
公共身份验证身份验证(身份验证身份验证)引发身份验证异常{
SmsCodeAuthenticationToken authenticationToken=(SmsCodeAuthenticationToken)身份验证;
String mobile=(String)authenticationtoken。get principal();
checkSmsCode(手机);
用户详细信息用户详细信息=用户详细信息服务。loaduserbysusername(手机);
//此时鉴权成功后,应当重新新的一个拥有鉴权的认证结果返回
SmsCodeAuthenticationToken authenticationResult=new SmsCodeAuthenticationToken(用户详细信息,用户详细信息。getauthorities());
认证结果。设置详细信息(authenticationtoken。获取详细信息());
返回认证结果
}
私有void checkSmsCode(字符串移动){
http servlet请求request=((ServletRequestAttributes)requestcontextholder。getrequestattributes()).get request();
//从会议中获取图片验证码
SMS code smsCodeInSession=(SMS code)会话策略。get属性(新servlet webrequest(请求),SESSION _ KEY _ PREFIX);
字符串输入代码=请求。getparameter( SMS代码);
if(smsCodeInSession==null) {
抛出新的BadCredentialsException(未检测到申请验证码);
}
字符串mobile session=smscodeinsession。get mobile();
如果(!Objects.equals(mobile,mobileSsion)) {
抛出新的BadCredentialsException(手机号码不正确);
}
字符串代码session=smscodeinsession。get code();
如果(!对象。equals(代码会话,输入代码)){
抛出新的BadCredentialsException(验证码错误);
}
}
@覆盖
公共布尔支持(类。认证){
//判断证明是不是SmsCodeAuthenticationToken的子类或子接口
返回smscodeauthenticationtoken。班级。isassignablefrom(身份验证);
}
public UserDetailsService getUserDetailsService(){
返回用户详细信息服务
}
public void setUserDetailsService(UserDetailsService UserDetailsService){
这个。用户详细信息服务=用户详细信息服务;
}
}
4、SmsCodeAuthenticationSecurityConfig
既然自定义了拦截器,可以需要在配置里做改动。
代码
@组件
公共类SmsCodeAuthenticationSecurityConfig扩展SecurityConfigurerAdapterDefaultSecurityFilterChain,HttpSecurity {
@自动连线
私有SmsUserService
@自动连线
private AuthenctiationSuccessHandler AuthenctiationSuccessHandler;
@自动连线
private AuthenctiationFailHandler AuthenctiationFailHandler;
@覆盖
公共空的配置(HttpSecurity http) {
SmsCodeAuthenticationFilter SmsCodeAuthenticationFilter=new SmsCodeAuthenticationFilter();
smscodeauthenticationfilter。setauthenticationmanager(http。getsharedobject(authenticationmanager。类));
smscodeauthenticationfilter。setauthenticationsuccesshandler(authenctiationSuccessHandler);
smscodeauthenticationfilter。setauthenticationfailurehandler(authenctiationFailHandler);
SmsCodeAuthenticationProvider SmsCodeAuthenticationProvider=new SmsCodeAuthenticationProvider();
//需要将通过用户名查询用户信息的接口换成通过手机号码实现
smscodeauthenticationprovider。setuserdetailsservice(smsUserService);
http。认证提供者(smsCodeAuthenticationProvider)。addFilterAfter(smsCodeAuthenticationFilter,usernamepasswordtuthenticationfilter。类);
}
}
5、短信用户服务
因为用户名,密码登陆最终是通过用户名查询用户信息,而手机验证码登陆是通过手机登陆,所以这里需要自己再实现一个短信用户服务
@服务
@Slf4j
公共类SMS用户服务实现用户详细信息服务{
@自动连线
专用用户映射程序用户映射程序;
@自动连线
私有角色用户映射角色用户映射;
@自动连线
私有角色映射角色映射;
/**
* 手机号查询用户
*/
@覆盖
公共用户详细信息loaduserbysusername(字符串移动)抛出UsernameNotFoundException {
log.info(手机号查询用户,手机号码={} ,移动);
//TODO这里我没有写sql通过手机号查询用户信息,因为刚建用户表的时候没有建移动字段,现在暂时不想加。
//TODO,所以暂时用用户名查询用户信息(懂就行)
user=user mapper . findonebysusername( little );
if (user==null) {
抛出新的UserNameNotFundException(“未找到用户信息”);
}
//获取用户关联的角色信息。如果为空,则用户不与角色相关联。
ListRolesUser userList=rolesusermapper . findallbyuid(user . getid());
if(collection utils . isempty(userList)){
返回用户;
}
//获取角色id集
list integer ridList=userlist . stream()。map(RolesUser:getRid)。collect(collectors . to list());
list roles roles list=roles mapper . findbyidin(ridList);
//插入用户角色信息
user . set roles(roles list);
返回用户;
}
}
6.摘要
这里,思路非常清晰。我是来总结的。
1.首先,从获得验证开始,当前的验证码信息已经保存到会话中。这些信息包括验证码和手机号码。
2.用户输入验证登录。在这里,它直接写在SmsAuthenticationFilter中。先验证验证码和手机号是否正确,再查询用户信息。我们也可以打开它作为用户名和密码登录。
过滤器专门验证验证码和手机号是否正确,通过取验证码正确登录过滤器。
3.SmsAuthenticationFilter的过程中还有一个关键步骤,即用户名和密码的登录是一个自定义的UserService。UserDetailsService实现后,可以通过用户名查询用户名信息,这里是
可以通过手机号查询用户信息,所以还需要定制SmsUserService来实现UserDetailsService。
三、测试
1、获取验证码获取验证码的手机号是15612345678。因为这里没有第三方的短信SDK,所以只是在后台输出。
向手机号为15612345678的用户发送验证码:254792。
2、登陆1)验证码输入不正确
发现登录失败,如果手机号输入错误,也是登录失败。
2)登录成功。
当手机号和短信验证码正确时,登录成功。
涉及
1.Spring安全技术栈开发企业认证和授权(JoJo)
2.实现短信验证码功能的SpringSceurity示例代码
关于SpringSceurity的短信验证码落地的这篇文章到此为止。关于SpringSceurity短信验证码登陆的更多信息,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!