本文主要介绍了通过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短信验证码登陆的更多信息,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。