現(xiàn)在驗證碼登錄已經(jīng)成為很多應(yīng)用的主流登錄方式,但是對于OAuth2授權(quán)來說,手機號驗證碼處理用戶認(rèn)證就非常繁瑣,很多同學(xué)卻不知道怎么接入。
認(rèn)真研究胖哥Spring Security OAuth2專欄的都會知道一個事,OAuth2其實不管資源擁有者是如何認(rèn)證的,只要資源擁有者在授權(quán)的環(huán)節(jié)中認(rèn)證了就可以了,至于你是驗證碼、賬密,甚至是什么指紋虹膜都無所謂。
Id Server實現(xiàn)
因此胖哥好像找到了將驗證碼接入Id Server的方式,前面胖哥開源了一個Spring Security的登錄擴展包spring-security-login-extension,可以一鍵接入驗證碼登錄和小程序登錄,利用這個應(yīng)該就能實現(xiàn)。因此我就改造了一番成功實現(xiàn)了這一功能。看下效果:
和之前相比,用戶在授權(quán)過程中可以選擇賬密登錄或者手機驗證碼登錄。
這里你變通一下,是不是只要是驗證碼登錄都可以兼容進(jìn)去了呢?
大致原理
這里需要前后端協(xié)同實現(xiàn)。
后端
核心還是擴展包的用法,給HttpSecurity
加入LoginFilterSecurityConfigurer
配置,這里我改動了一下和原來包中的不太一樣。這里登錄成功后不能再返回JWT
了,需要和賬密登錄保持一致,核心代碼如下:
httpSecurity.apply(new LoginFilterSecurityConfigurer<>())
// 手機號驗證碼登錄模擬
.captchaLogin(captchaLoginConfigurer ->
// 驗證碼校驗 1 在此處配置 優(yōu)先級最高 2 注冊為Spring Bean 可以免配置
captchaLoginConfigurer.captchaService(this::verifyCaptchaMock)
// 根據(jù)手機號查詢用戶UserDetials 1 在此處配置 優(yōu)先級最高 2 注冊為Spring Bean 可以免配置
.captchaUserDetailsService(this::loadUserByPhoneMock)
// 兩個登錄保持一致
.successHandler(loginAuthenticationSuccessHandler)
// 兩個登錄保持一致
.failureHandler(authenticationFailureHandler);
其中loadUserByPhoneMock
是模擬CaptchaUserDetailsService
接口,根據(jù)手機號加載UserDetails
:
private UserDetails loadUserByPhoneMock(String phone) throws UsernameNotFoundException {
return // 用戶名
User.withUsername(phone)
// 密碼
.password("password") .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
.roles("user", "mobile")
.build();
}
verifyCaptchaMock
是驗證碼校驗邏輯接口CaptchaService
的模擬,這里寫死為1234
,實際開發(fā)中應(yīng)該用緩存實現(xiàn),從發(fā)碼接口中存入緩存,在CaptchaService
中調(diào)用緩存接口取出驗證碼進(jìn)行校驗:
private boolean verifyCaptchaMock(String phone, String code) {
//todo 自己實現(xiàn)緩存校驗邏輯
return code.equals("1234");
}
驗證碼發(fā)送的接口自由實現(xiàn),這里不需要去定義規(guī)范,記得接入緩存就行了。
前端
前端只需要接入一個可以切換登錄方式的登錄頁就行了。然后把驗證碼登錄接口和發(fā)送驗證碼接口配進(jìn)去就行了,授權(quán)登錄頁面為oauth2_login.html
,通過其控制器,胖哥甚至加了一個開關(guān)enableCaptchaLogin
來決定是否使用驗證碼認(rèn)證方式。
@GetMapping("/login")
public String oauth2LoginPage(Model model,
@CurrentSecurityContext(expression = "authentication")
Authentication authentication,
@Value("${spring.security.oauth2.server.login.captcha.enabled:true}")
boolean enableCaptchaLogin,
@RequestAttribute(name = "org.springframework.security.web.csrf.CsrfToken", required = false)
CsrfToken csrfToken) {
if (!(authentication instanceof AnonymousAuthenticationToken)){
return "redirect:/";
}
if (csrfToken != null) {
model.addAttribute("_csrfToken", csrfToken);
}
SystemSettings systemSettings = new SystemSettings();
model.addAttribute("enableCaptchaLogin",enableCaptchaLogin);
model.addAttribute("systemSettings", systemSettings);
return "oauth2_login";
}
前端和驗證碼相關(guān)的JS處理:
if ([[${enableCaptchaLogin}]]){
form.on('submit(mobile-login)', function (data) {
let loader = layer.load();
let btn = button.load({elem: '.login'});
$.ajax({
url: '/login/captcha',
data: data.field,
type: "post",
dataType: 'json',
success: function (result) {
layer.close(loader);
btn.stop(function () {
if (result.code === 200) {
popup.success(result.msg, function () {
location.href = result.data.targetUrl;
})
} else if (result.code === 401) {
popup.failure(result.msg);
}
})
}
});
return false;
});
$('#captcha-btn').click(function (){
//TODO 這里接入驗證碼接口
popup.success('驗證碼已發(fā)送');
})
}
關(guān)于Id Server
Id Server是一個基于Spring Authorization Server的開源的授權(quán)服務(wù)器,大大降低OAuth2授權(quán)服務(wù)器的學(xué)習(xí)使用難度,提供UI控制臺,動態(tài)權(quán)限控制,方便OAuth2客戶端管理,可以一鍵生成Spring Security配置,開箱即用,支持集成Spring Boot、Spring Cloud等java生態(tài)的框架,甚至支持其它語言,少量配置就可部署,代碼開源,方便二次開發(fā),支持OAuth2四種客戶端認(rèn)證方式和三種授權(quán)模式,支持賬密認(rèn)證和驗證碼認(rèn)證。歡迎學(xué)習(xí)使用并參與代碼貢獻(xiàn)。
總結(jié)
OAuth2使用驗證碼進(jìn)行授權(quán)已經(jīng)實現(xiàn)了,適用于所有Id Server提供的DEMO。如果有興趣可以從以下倉庫地址獲取最新的驗證碼授權(quán)代碼,記得給個Star哦:
https://github.com/NotFound403/id-server
另外還有人問Id Server和胖哥Spring Security OAuth2專欄的關(guān)系,Id Server是一個開源項目,底層的邏輯支撐來自對Spring Authorization Server的分析,掌握專欄的知識可以幫助你對Id Server的自定義改造,Id Server目標(biāo)是打造一個生產(chǎn)可用的OAuth2授權(quán)服務(wù)器,降低OAuth2的學(xué)習(xí)使用成本,希望大家多多支持。
關(guān)注公眾號:碼農(nóng)小胖哥,獲取更多資訊