前言
1、本文默認各位同學已經整合好密碼模式。
2、沒有整合好也沒關系,可以參考架構搭建中的文章來走通基礎搭建過程
不想看分析,想直接簡單粗暴開始干的請直接跳到標題為編碼階段的開始看
架構搭建
本文只說驗證碼登錄相關部分,默認大家Spring Cloud OAuth2這部分環境已經搭建好。
https://www.cnblogs.com/fengzheng/p/11724625.html
需求
OAuth 2 有四種授權模式,分別是授權碼模式(authorization code)、簡化模式(implicit)、密碼模式(resource owner password credentials)、客戶端模式(client credentials)。
但是有時候我們需要一些自己特殊的模式登錄,比如說驗證碼登錄,第三方登錄等等。上面好像並不是很方便,接下來我會將密碼模式改造成驗證碼方式來登錄。
思路分析
TokenEndpoint類中可以看到入口/oauth/token

首先我們要知道/oauth/token是我們登錄驗證調用的接口,無論是get還是post都會走到post這個方法中。
其次我們需要打斷點去看一下源碼,了解兩件事。
1、哪一步加入的四種授權模式。(因為四種授權模式是寫死在源碼里的,拓展的時候我們得自己加上)
2、密碼模式是在哪里開始驗證用戶名密碼。(為的是改造那部分來寫自己的驗證碼模式)
看源碼階段

OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
這句代碼就是我們要找的突破點
this.getTokenGranter()會調用this.tokenGranter(),

tokenGranter的內容為:

this.getDefaultTokenGranters()
就是這個方法,寫死了那四種授權模式。紅圈部分是在添加密碼模式,之后我們就是要在這里添加我們的授權碼模式

密碼模式實現類ResourceOwnerPasswordTokenGranter

ResourceOwnerPasswordTokenGranter繼承了AbstractTokenGranter類
而AbstractTokenGranter的實現類正好對應着四種授權模式,外加個刷新token的

那么改造思路就很明確了
1、我們自己新增一個驗證碼模式類繼承AbstractTokenGranter,
2、在源碼調用this.getDefaultTokenGranters()方法的時候我們手動把這個類加進去不就行了
編碼階段
新建自定義驗證碼類SmsTokenGranter
內容直接復制ResourceOwnerPasswordTokenGranter中的內容

password換成我們自定義的sms


重點在於this.checkPhoneSms(parameters);這是我們自定義的驗證方法,對於驗證碼的驗證就在這個方法中去做
//在這個類中無法使用@autowire來從spring容器中取bean,因為不是他管理的,
//所以我們寫了一個工具類(就是附錄中的ApplicationContextAwareUtil)去取我們需要的bean
public UserInfoService userInfoService = ApplicationContextAwareUtil.getBean("userInfoServiceImpl");
public StringRedisTemplate stringRedisTemplate = ApplicationContextAwareUtil.getBean("stringRedisTemplate");
/**
* 自定義手機驗證碼校驗
*/
public void checkPhoneSms(Map<String, String> 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獲取用戶的權限信息組裝起來

之所以加冒號區分是因為,我們在checkPhoneSms自定義方法中根據手機號拿到的密碼,是我已經加密過的,數據庫中不會存真實的密碼的。
問題在於原本的密碼模式,是直接拿到我們輸入的密碼然后用passwordEncoder加密后再繼續處理的,改成手機登錄后我們拿到的就是一個加密的密碼,所以它又會加密一次。所以在loadUserByUsername方法中,我們發現是其他模式登錄時,把password也再加密一次。這樣對比的密碼就一致了。
在源碼中加入我們自定義的驗證碼類
方式有好幾種,我就提供一種簡單粗暴的,有想法的同學可以自己更改方式

我這里是從源碼中把AuthorizationServerEndpointsConfigurer類復制出來,放在同名的包下,這樣啟動項目后就會優先走我們修改的代碼
修改的東西很簡單,把密碼模式那句話復制一下,把new的類從ResourceOwnerPasswordTokenGrantercoin換成我們自定義的,SmsTokenGranter類即可,參數不用改變。

修改OAuth2 的認證中心配置文件
加上sms,這樣在后面查找授權模式的時候才能找到

調用測試
輸入一個錯誤的驗證碼

輸入一個正確的驗證碼
在redis中我們預設值一個


最后就能驗證成功了
附錄
ApplicationContextAwareUtil
/**
* 存在一些情況無法直接Autowired注入我們需要的類,通過此工具類則可以直接獲取spring中的bean
* <p>
* 根據類名獲取實例,例:
* 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> T getBean(String name) throws BeansException {
if (applicationContext == null) {
return null;
}
return (T) applicationContext.getBean(name);
}
}
參考文章
https://www.cnblogs.com/fengzheng/p/11724625.html
https://blog.csdn.net/qq213539/article/details/84312431