Spring Security 一鍵接入驗證碼登入和小程式登入

碼農小胖哥發表於2022-04-03

最近實現了一個多端登入的Spring Security元件,用起來非常絲滑,開箱即用,可插拔,而且靈活性非常強。我覺得能滿足大部分場景的需要。目前完成了手機號驗證碼和微信小程式兩種自定義登入,加上預設的Form登入,一共三種,現在開源分享給大家,接下來簡單介紹一下這個外掛包。

DSL配置風格

切入正題,先來看看配置:

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .mvcMatchers("/foo/**")
                .access("hasAuthority('ROLE_USER')").anyRequest().authenticated()
                .and()
                // 預設form表單登入
                .formLogin()
                .and()
                .apply(new LoginFilterSecurityConfigurer<>())
                // 驗證碼登入
                .captchaLogin(captchaLoginConfigurer ->
                                // 驗證碼校驗 1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                captchaLoginConfigurer.captchaService(this::verifyCaptchaMock)
                                        // 根據手機號查詢使用者UserDetials  1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                        .captchaUserDetailsService(this::loadUserByPhoneMock)
                                        // 生成JWT 返回  1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                        .jwtTokenGenerator(this::tokenResponseMock)
                        //todo 其它配置省略……
                )
                // 小程式登入 同時支援多個小程式
                .miniAppLogin(miniAppLoginConfigurer -> miniAppLoginConfigurer
                                // 實現小程式多租戶
                                // 根據請求攜帶的clientid 查詢小程式的appid和secret 1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                .miniAppClientService(this::miniAppClientMock)
                                // 小程式使用者 自動註冊和檢索  1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                .miniAppUserDetailsService(new MiniAppUserDetailsServiceMock())
                                // 小程式sessionkey快取 過期時間應該小於微信官方文件的宣告   1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                .miniAppSessionKeyCache(new MiniAppSessionKeyCacheMock())
                                // 生成JWT 返回  1 在此處配置 優先順序最高 2 註冊為Spring Bean 可以免配置
                                .jwtTokenGenerator(this::tokenResponseMock)
                        //todo 其它配置省略……
                );

        return http.build();
    }

這種風格完全貼合了Spring SecurityDSL配置風格,不僅僅高大上,而且可以按需配置。如果你沒有驗證碼登入直接刪掉captchaLogin方法;如果你沒有微信小程式登入直接刪掉miniAppLogin方法。甚至還可以對單種登入進行細粒度定製化,formLogin有的功能基本驗證碼登入和微信小程式登入的都有。

為什麼這麼靈活?

這裡抽象了一個登入配置類:

 public abstract class AbstractLoginFilterConfigurer<H extends HttpSecurityBuilder<H>, 
         C extends AbstractLoginFilterConfigurer<H, C, F>, 
         F extends AbstractAuthenticationProcessingFilter>
         extends AbstractHttpConfigurer<AbstractLoginFilterConfigurer<H, C, F>, H> {
             // 省略……
         }

所有額外的登入渠道大都可以通過這個類來擴充套件,負責驗證碼登入的CaptchaLoginFilterConfigurer和微信小程式登入的MiniAppLoginFilterConfigurer都是該類實現的,基本上你看了原始碼也能照葫蘆畫瓢來一個。

另外上面這些配置項介面,都可以放在Spring IoC中,配置類能自動獲取,不過優先順序最高的還是通過上面程式碼中配置的具體實現,原理參見下面的的樣例:

  @Override
     protected AuthenticationSuccessHandler defaultSuccessHandler(H http) {
         // 如果配置類沒有配置 就嘗試去Spring IoC中發現
         if (this.jwtTokenGenerator == null) {
             ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
             jwtTokenGenerator = getBeanOrNull(applicationContext, JwtTokenGenerator.class);
         }
         Assert.notNull(jwtTokenGenerator, "jwtTokenGenerator is required");
         return new LoginAuthenticationSuccessHandler(jwtTokenGenerator);
     }
 ​
 ​
     public final <T> T getBeanOrNull(ApplicationContext applicationContext, Class<T> beanType) {
         String[] beanNames = applicationContext.getBeanNamesForType(beanType);
         if (beanNames.length == 1) {
             return applicationContext.getBean(beanNames[0], beanType);
         }
         return null;
     }

使用方法

自行使用Maven命令mvn install到本地倉庫,然後引入:

        <dependency>
            <groupId>cn.felord</groupId>
            <artifactId>spring-security-extension</artifactId>
            <version>1.0.0</version>
        </dependency>

然後參考樣例sample專案進行開發,登入方式有三種。

普通登入

原生Spring Security介面

POST /login?username=user&password=12345 HTTP/1.1
Host: localhost:8080

驗證碼登入

需要先實現必須的配置介面

傳送驗證碼後呼叫驗證碼登入介面:


POST /login/captcha?phone=11111111111&captcha=123123 HTTP/1.1
Host: localhost:8080

小程式登入

需要先實現必須的配置介面

前端先呼叫微信授權登入介面獲取openid:


POST /miniapp/preauth?clientId=wxxda23234&jsCode=051A23234ZHa1tZ5yj3AOlFr HTTP/1.1
Host: localhost:8080

響應:

{
    "code": 200,
    "data": {
        "errcode": null,
        "errmsg": null,
        "sessionKey": null,
        "openid": "oWmZj5QBrZxxxxx8OUxRrZJi4",
        "unionid": "oS-dxxxxxx4w_x7dA-h9MIuA"
    },
    "msg": "",
    "identifier": true
}

然後呼叫小程式登入介面:

POST /login/miniapp HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "clientId": "wxd14qr6",
    "openId": "oWmZj5QBrZIBks0xx8OUxRrZJi4",
    "unionId": "oS-dK520tgW8xxxx7dA-h9MIuA",
    "iv":"LQUOt8BSTa7xxxpe1Q==",
    "encryptedData": "10hn3o4xxxxxrO/Ag5nRD3QkLSzduKnWuzN9B/H4Y0G5mDPR8siA7T8yaaqZsrMycLAoe2qrd1J75yYetYuWifiq3jUrcceRZHVxxl9LnQdW8f5+pMTnQtCYiMJ7Jm9paCw2Bh+5Lowkyqkx1q0fALvCQ9LXPPLAbLOB9CavRfKoenAmyyHQjZ/6lz0njzA=="
}

獲取方式

Gitee: felord/spring-security-login-extension

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

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

相關文章