SpringSceurity实现短信验证码登陆

SpringSceurity实现短信验证码登陆

本文主要介绍了通过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短信验证码登陆的更多信息,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!

SpringSceurity实现短信验证码登陆