Spring Boot,Spring Security實現(xiàn)OAuth2 + JWT認(rèn)證

閱讀此文,希望是對JWT以及OAuth2有一定了解的童鞋。

JWT認(rèn)證,提供了對稱加密以及非對稱的實現(xiàn)。

內(nèi)容源碼點我

涉及到源碼中兩個服務(wù)

spring-boot-oauth-jwt-server

spring-boot-oauth-jwt-resource-server


認(rèn)證服務(wù)端

提供認(rèn)證、授權(quán)服務(wù)

實現(xiàn)方式,主要復(fù)寫AuthorizationServerConfigurerAdapter實現(xiàn)

認(rèn)證服務(wù)1-對稱加密方式

對稱加密,表示認(rèn)證服務(wù)端和認(rèn)證客戶端的共用一個密鑰

實現(xiàn)方式

  • AccessToken轉(zhuǎn)換器-定義token的生成方式,這里使用JWT生成token,對稱加密只需要加入key等其他信息(自定義)。
          @Bean
          public JwtAccessTokenConverter accessTokenConverter() {
              JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
              converter.setSigningKey("123");
              return converter;
          }
  • 告訴spring security token的生成方式
        @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                endpoints.tokenStore(tokenStore())
                         .accessTokenConverter(accessTokenConverter())
                         .authenticationManager(authenticationManager);
            }

以上對稱加密的JWT方式的認(rèn)證服務(wù)端就OK了,后面有對應(yīng)的資源服務(wù)端的內(nèi)容。

認(rèn)證服務(wù)2-非對稱加密方式(公鑰密鑰)

服務(wù)端生成公鑰和密鑰,每個客戶端使用獲取到的公鑰到服務(wù)器做認(rèn)證。
因此首先要生成一個證書,導(dǎo)出公鑰再后續(xù)步驟

實現(xiàn)方式

  • 生成JKS文件

      keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore mytest.jks -storepass mypass
    
jks

具體參數(shù)的意思不另說明。

  • 導(dǎo)出公鑰

      keytool -list -rfc --keystore mytest.jks | openssl x509 -inform pem -pubkey
    
publicKey
  • 生成公鑰文本

      -----BEGIN PUBLIC KEY-----
      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhAF1qpL+8On3rF2M77lR
      +l3WXKpGXIc2SwIXHwQvml/4SG7fJcupYVOkiaXj4f8g1e7qQCU4VJGvC/gGJ7sW
      fn+L+QKVaRhs9HuLsTzHcTVl2h5BeawzZoOi+bzQncLclhoMYXQJJ5fULnadRbKN
      HO7WyvrvYCANhCmdDKsDMDKxHTV9ViCIDpbyvdtjgT1fYLu66xZhubSHPowXXO15
      LGDkROF0onqc8j4V29qy5iSnx8I9UIMEgrRpd6raJftlAeLXFa7BYlE2hf7cL+oG
      hY+q4S8CjHRuiDfebKFC1FJA3v3G9p9K4slrHlovxoVfe6QdduD8repoH07jWULu
      qQIDAQAB
      -----END PUBLIC KEY-----
    

存儲為public.txt。把 mytest.jks和public.txt放入resource目錄下

  • 這里我們要修改JwtAccessTokenConverter,把證書導(dǎo)入
        @Bean
        public TokenEnhancer accessTokenConverter() {
            final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            KeyStoreKeyFactory keyStoreKeyFactory =
                    new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray());
            converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
        
            converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
            return converter;
        }

以上,就可以實現(xiàn)非對稱加密了

額外信息(這部分信息不關(guān)乎加密方式)

  • 自定義生成token攜帶的信息

有時候需要額外的信息加到token返回中,這部分也可以自定義,此時我們可以自定義一個TokenEnhancer
TokenEnhancer 接口提供一個 enhance(OAuth2AccessToken var1, OAuth2Authentication var2) 方法,用于對token信息的添加,信息來源于 OAuth2Authentication

這里我們加入了用戶的授權(quán)信息。

            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                final Map<String, Object> additionalInfo = new HashMap<>();
                User user = (User) authentication.getUserAuthentication().getPrincipal();
                additionalInfo.put("username", user.getUsername());
                additionalInfo.put("authorities", user.getAuthorities());
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
                return accessToken;
            }

同樣要告訴spring security,我們把這個TokenEnhancer加入到TokenEnhancer鏈中(鏈,所以可以好多個)

        // 自定義token生成方式
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customerEnhancer(), accessTokenConverter()));
        endpoints.tokenEnhancer(tokenEnhancerChain);
  • 自定義token信息中添加的信息

JWT中,需要在token中攜帶額外的信息,這樣可以在服務(wù)之間共享部分用戶信息,spring security默認(rèn)在JWT的token中加入了user_name,如果我們需要額外的信息,需要自定義這部分內(nèi)容。

JwtAccessTokenConverter是我們用來生成token的轉(zhuǎn)換器,所以我們需要配置這里面的部分信息來達(dá)到我們的目的。
JwtAccessTokenConverter默認(rèn)使用DefaultAccessTokenConverter來處理token的生成、裝換、獲取。DefaultAccessTokenConverter中使用UserAuthenticationConverter來對應(yīng)處理token與userinfo的獲取、轉(zhuǎn)換。因此我們需要重寫下UserAuthenticationConverter對應(yīng)的轉(zhuǎn)換方法就可以

        @Override
        public Map<String, ?> convertUserAuthentication(Authentication authentication) {
            LinkedHashMap response = new LinkedHashMap();
            response.put("user_name", authentication.getName());
            response.put("name", ((User) authentication.getPrincipal()).getName());
            response.put("id", ((User) authentication.getPrincipal()).getId());
            response.put("createAt", ((User) authentication.getPrincipal()).getCreateAt());
            if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
                response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
            }
        
            return response;
        }

告訴JwtAccessTokenConverter ,把我們的方式替換默認(rèn)的方式

        @Bean
        public TokenEnhancer accessTokenConverter() {
            final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey("123");
            converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
            return converter;
        }

資源服務(wù)端

實現(xiàn)方式,主要復(fù)寫ResourceServerConfigurerAdapter實現(xiàn)

資源服務(wù)1-對稱加密方式

此處配置與認(rèn)證服務(wù)基本一致,不同的是,資源服務(wù)器配置是在ResourceServerConfigurerAdapter做配置,其他的看源碼吧,大差不差。

資源服務(wù)2-非對稱加密方式(公鑰)

把 public.txt放入resource目錄下
修改JwtAccessTokenConverter如下:

        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            Resource resource = new ClassPathResource("public.txt");
            String publicKey = null;
            try {
                publicKey = inputStream2String(resource.getInputStream());
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
            converter.setVerifierKey(publicKey);
            converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
            return converter;
        }

然后就可以跑起來了。

效果驗證

token獲取

code獲?。?/strong>

http://127.0.0.1:8081/oauth/authorize?client_id=clientId&response_type=code&redirect_uri=http://127.0.0.1:8082/login/my

redirect_uri:需要與配置在認(rèn)證服務(wù)器中的一致。
client_id:client_id也是預(yù)先在認(rèn)證服務(wù)器中,這里是保存在數(shù)據(jù)庫里的
response_type:寫死code

瀏覽器進(jìn)入后的頁面。


密碼驗證

輸入賬號密碼,這個也是保存在數(shù)據(jù)庫,默認(rèn)是保存在內(nèi)存中。

授權(quán)之后就可以獲得code

http://127.0.0.1:8082/login/my?code=rTKETX

根據(jù)這個code,POST下獲取token

http://127.0.0.1:8081/oauth/token?grant_type=authorization_code&code=rTKETX&redirect_uri=http://127.0.0.1:8082/login/my&client_id=clientId&client_secret=secret
token獲取請求

grant_type:這里寫死authorization_code
code:上面得到的rTKETX
redirect_uri:同上不變
client_id:同上不變
client_secret:對應(yīng)的密碼

結(jié)果返回如下:


token獲取內(nèi)容

token驗證

http://127.0.0.1:8081/oauth/check_token?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

驗證token

用戶信息獲取

用戶信息

資源服務(wù)端就不展示了...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容