使用java获取四种验证码_荐 Spring Cloud OAuth2实现手机验证码登录...

该博客介绍了如何在Spring Cloud OAuth2环境下改造密码模式,实现验证码登录。作者详细讲解了改造思路,包括分析源码,创建自定义验证码类SmsTokenGranter,并在源码中添加验证码模式,最后通过测试验证了验证码登录的正确性。
摘要由CSDN通过智能技术生成

前言

1、本文默认各位同学已经整合好密码模式。

2、没有整合好也没关系,可以参考架构搭建中的文章来走通基础搭建过程

不想看分析,想直接简单粗暴开始干的请直接跳到标题为编码阶段的开始看

架构搭建

本文只讲验证码登录相关部分,默认大家Spring Cloud OAuth2这部分环境已经搭建好。

需求

OAuth 2 有四种授权模式,分别是授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner password credentials)、客户端模式(client credentials)。

但是有时候我们需要一些自己特殊的模式登录,比如说验证码登录,第三方登录等等。上面好像并不是很方便,​接下来我会将密码模式改造成验证码方式来登录。

思路分析

TokenEndpoint类中可以看到入口/oauth/token

7af20631cdcce5960c7bde34beb3bc28.png

首先我们要知道/oauth/token是我们登录验证调用的接口,无论是get还是post都会走到post这个方法中。

其次我们需要打断点去看一下源码,了解两件事。

1、哪一步加入的四种授权模式。(因为四种授权模式是写死在源码里的,拓展的时候我们得自己加上)

2、密码模式是在哪里开始验证用户名密码。(为的是改造那部分来写自己的验证码模式)

看源码阶段

b664227e2646b62c2376a30794587c8b.png

OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

这句代码就是我们要找的突破点

this.getTokenGranter()会调用this.tokenGranter(),

cc2d1ef1bbbccc57b44f014c646e2df4.png

tokenGranter的内容为:

95df13a905bf09bed51ea6f2dbde6488.png

this.getDefaultTokenGranters()

就是这个方法,写死了那四种授权模式。红圈部分是在添加密码模式,之后我们就是要在这里添加我们的授权码模式

286e1c647c72f1ff9a72c556b1e222b4.png

密码模式实现类ResourceOwnerPasswordTokenGranter

ca3f06dacf58c27f04b613f6c73192e6.png

ResourceOwnerPasswordTokenGranter继承了AbstractTokenGranter类

而AbstractTokenGranter的实现类正好对应着四种授权模式,外加个刷新token的

eb3a3b564d82c691371766547665e599.png

那么改造思路就很明确了

1、我们自己新增一个验证码模式类继承AbstractTokenGranter,

2、在源码调用this.getDefaultTokenGranters()方法的时候我们手动把这个类加进去不就行了

编码阶段

新建自定义验证码类SmsTokenGranter

内容直接复制ResourceOwnerPasswordTokenGranter中的内容

369fda2e32f1400aced4ba2a4e50dddc.png

password换成我们自定义的sms

a14ddf1b0929e6d1b6703fbb85cd93ca.png

50cd7f2476851b27ba221013d806fdf8.png

重点在于this.checkPhoneSms(parameters);这是我们自定义的验证方法,对于验证码的验证就在这个方法中去做

//在这个类中无法使用@autowire来从spring容器中取bean,因为不是他管理的,

//所以我们写了一个工具类(就是附录中的ApplicationContextAwareUtil)去取我们需要的bean

public UserInfoService userInfoService = ApplicationContextAwareUtil.getBean("userInfoServiceImpl");

public StringRedisTemplate stringRedisTemplate = ApplicationContextAwareUtil.getBean("stringRedisTemplate");

/**

* 自定义手机验证码校验

*/

public void checkPhoneSms(Map parameters) {

String phone = (String) parameters.get("phone");

String code = (String) parameters.get("code");//验证码

if (StringUtils.isBlank(phone)) {

throw new InvalidGrantException("手机号不能为空");

}

if (StringUtils.isBlank(code)) {

throw new InvalidGrantException("验证码不能为空");

}

String codeCache = stringRedisTemplate.opsForValue().get("sms:" + phone);

if (StringUtils.isBlank(codeCache)) {

throw new InvalidGrantException("验证码已失效,请重新获取");

}

if (!code.equals(codeCache)) {

throw new InvalidGrantException("验证码错误");

}

/**

1、从缓存中根据手机号取出验证码验证

2、验证通过后根据手机号查出用户名密码,设置到parameters中,这样本质就还是走密码模式

(如果未创建的用户在这一步可以调用创建用户的方法,同时随机生成一个密码存着)。

特别注意:设置password时,根据自己的实际情况决定是否要使用PasswordEncoder,否则密码验证

时会一直报错。不清楚PasswordEncoder是啥的同学,建议先阅读下面链接的环境搭建

https://www.cnblogs.com/fengzheng/p/11724625.html

*/

//设置username和password,之所以加个冒号

//是为了在loadUserByUsername方法中和原本走密码模式的username做一个区分

parameters.put("username", userInfo.getUsername() + ":");

parameters.put("password", password);

}

loadUserByUsername方法中会根据username获取用户的权限信息组装起来

042f2c9d9682b6a15e5f169715adae22.png

之所以加冒号区分是因为,我们在checkPhoneSms自定义方法中根据手机号拿到的密码,是我已经加密过的,数据库中不会存真实的密码的。

问题在于原本的密码模式,是直接拿到我们输入的密码然后用passwordEncoder加密后再继续处理的,改成手机登录后我们拿到的就是一个加密的密码,所以它又会加密一次。所以在loadUserByUsername方法中,我们发现是其他模式登录时,把password也再加密一次。这样对比的密码就一致了。

在源码中加入我们自定义的验证码类

方式有好几种,我就提供一种简单粗暴的,有想法的同学可以自己更改方式

b2f712fdeaf9e9485d13a933204b6e16.png

我这里是从源码中把AuthorizationServerEndpointsConfigurer类复制出来,放在同名的包下,这样启动项目后就会优先走我们修改的代码

修改的东西很简单,把密码模式那句话复制一下,把new的类从ResourceOwnerPasswordTokenGrantercoin换成我们自定义的,SmsTokenGranter类即可,参数不用改变。

88b22d241b8e374f739c2632d545e815.png

修改OAuth2 的认证中心配置文件

加上sms,这样在后面查找授权模式的时候才能找到

5382cba0ece5e244affebb9be3d244c0.png

调用测试

输入一个错误的验证码

fb07d4eff1f7857954ffb74b760b67da.png

输入一个正确的验证码

在redis中我们预设值一个

0d9ab4b6f956a9419d1fbd03b5078089.png

41a42b3b8e387dc096587bb5dcdf655f.png

最后就能验证成功了

附录

ApplicationContextAwareUtil

/**

* 存在一些情况无法直接Autowired注入我们需要的类,通过此工具类则可以直接获取spring中的bean

*

* 根据类名获取实例,例:

* public StringRedisTemplate stringRedisTemplate = ApplicationContextAwareUtil.getBean("stringRedisTemplate");

*/

@Component

public class ApplicationContextAwareUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

ApplicationContextAwareUtil.applicationContext = applicationContext;

}

public static ApplicationContext getApplicationContext() {

return applicationContext;

}

@SuppressWarnings("unchecked")

public static T getBean(String name) throws BeansException {

if (applicationContext == null) {

return null;

}

return (T) applicationContext.getBean(name);

}

}

参考文章

本文地址:https://blog.csdn.net/qq_38198581/article/details/107335615

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值