搞定了!OAuth2使用驗證碼進行授權

碼農小胖哥 發表於 2022-05-20

現在驗證碼登入已經成為很多應用的主流登入方式,但是對於OAuth2授權來說,手機號驗證碼處理使用者認證就非常繁瑣,很多同學卻不知道怎麼接入。

認真研究胖哥Spring Security OAuth2專欄的都會知道一個事,OAuth2其實不管資源擁有者是如何認證的,只要資源擁有者在授權的環節中認證了就可以了,至於你是驗證碼、賬密,甚至是什麼指紋虹膜都無所謂。

Id Server實現

因此胖哥好像找到了將驗證碼接入Id Server的方式,前面胖哥開源了一個Spring Security的登入擴充套件包spring-security-login-extension,可以一鍵接入驗證碼登入和小程式登入,利用這個應該就能實現。因此我就改造了一番成功實現了這一功能。看下效果:

搞定了!OAuth2使用驗證碼進行授權

和之前相比,使用者在授權過程中可以選擇賬密登入或者手機驗證碼登入。

這裡你變通一下,是不是隻要是驗證碼登入都可以相容進去了呢?

大致原理

這裡需要前後端協同實現。

後端

核心還是擴充套件包的用法,給HttpSecurity加入LoginFilterSecurityConfigurer配置,這裡我改動了一下和原來包中的不太一樣。這裡登入成功後不能再返回JWT了,需要和賬密登入保持一致,核心程式碼如下:

httpSecurity.apply(new LoginFilterSecurityConfigurer<>())
     // 手機號驗證碼登入模擬
         .captchaLogin(captchaLoginConfigurer ->
    // 驗證碼校驗 1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                 captchaLoginConfigurer.captchaService(this::verifyCaptchaMock)
   // 根據手機號查詢使用者UserDetials  1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                          .captchaUserDetailsService(this::loadUserByPhoneMock)
                          // 兩個登入保持一致
                          .successHandler(loginAuthenticationSuccessHandler)
                          // 兩個登入保持一致
                          .failureHandler(authenticationFailureHandler);

其中loadUserByPhoneMock是模擬CaptchaUserDetailsService介面,根據手機號載入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,實際開發中應該用快取實現,從發碼介面中存入快取,在CaptchaService中呼叫快取介面取出驗證碼進行校驗:

private boolean verifyCaptchaMock(String phone, String code) {
    //todo 自己實現快取校驗邏輯
            return code.equals("1234");
}

驗證碼傳送的介面自由實現,這裡不需要去定義規範,記得接入快取就行了。

前端

前端只需要接入一個可以切換登入方式的登入頁就行了。然後把驗證碼登入介面傳送驗證碼介面配進去就行了,授權登入頁面為oauth2_login.html,通過其控制器,胖哥甚至加了一個開關enableCaptchaLogin來決定是否使用驗證碼認證方式。

@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";
}

前端和驗證碼相關的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('驗證碼已傳送');
            })
        }

關於Id Server

Id Server是一個基於Spring Authorization Server的開源的授權伺服器,大大降低OAuth2授權伺服器的學習使用難度,提供UI控制檯,動態許可權控制,方便OAuth2客戶端管理,可以一鍵生成Spring Security配置,開箱即用,支援整合Spring Boot、Spring Cloud等java生態的框架,甚至支援其它語言,少量配置就可部署,程式碼開源,方便二次開發,支援OAuth2四種客戶端認證方式和三種授權模式,支援賬密認證和驗證碼認證。歡迎學習使用並參與程式碼貢獻。

總結

OAuth2使用驗證碼進行授權已經實現了,適用於所有Id Server提供的DEMO。如果有興趣可以從以下倉庫地址獲取最新的驗證碼授權程式碼,記得給個Star哦:

https://github.com/NotFound403/id-server

另外還有人問Id Server和胖哥Spring Security OAuth2專欄的關係,Id Server是一個開源專案,底層的邏輯支撐來自對Spring Authorization Server的分析,掌握專欄的知識可以幫助你對Id Server的自定義改造,Id Server目標是打造一個生產可用的OAuth2授權伺服器,降低OAuth2的學習使用成本,希望大家多多支援。

關注公眾號:Felordcn 獲取更多資訊

個人部落格:https://felord.cn