擴展 jwt 解決 oauth2 性能瓶頸

oauth2 性能瓶頸

資源服務器的請求都會被攔截 到認證服務器校驗合法性 (以下圖)

  • 用戶攜帶token 請求資源服務器spring

  • 資源服務器攔截器 攜帶token 去認證服務器 調用tokenstore 對token 合法性校驗安全

  • 資源服務器拿到token,默認只會含有用戶名信息服務器

  • 經過用戶名調用userdetailsservice.loadbyusername 查詢用戶所有信息
    網絡

  • 如上步驟在實際使用,會形成認證中心的負載壓力過大,成爲形成整個系統瓶頸的關鍵點session


  • 圖片

check-token 過程當中涉及的源碼

  • 更爲詳細的源碼講解能夠參考我上篇文章《Spring Cloud OAuth2 資源服務器CheckToken 源碼解析》app

  • check-token 涉及到的核心類ide

  • 圖片

擴展jwt 生成攜帶用戶詳細信息

  • 爲何使用jwt 替代默認的UUID token ?
    經過jwt 訪問資源服務器後,再也不使用check-token 過程,經過對jwt 的解析便可實現身份驗證,登陸信息的傳遞。減小網絡開銷,提升總體微服務集羣的性能
    微服務

  • spring security oauth 默認的jwttoken 只含有username,經過擴展TokenEnhancer,實現關鍵字段的注入到 JWT 中,方便資源服務器使用性能

 
 
  1.    @Beanspa

  2.    public TokenEnhancer tokenEnhancer() {

  3.        return (accessToken, authentication) -> {

  4.            if (SecurityConstants.CLIENT_CREDENTIALS

  5.                    .equals(authentication.getOAuth2Request().getGrantType())) {

  6.                return accessToken;

  7.            }


  8.            final Map<String, Object> additionalInfo = new HashMap<>(8);

  9.            PigxUser pigxUser = (PigxUser) authentication.getUserAuthentication().getPrincipal();

  10.            additionalInfo.put("user_id", pigxUser.getId());

  11.            additionalInfo.put("username", pigxUser.getUsername());

  12.            additionalInfo.put("dept_id", pigxUser.getDeptId());

  13.            additionalInfo.put("tenant_id", pigxUser.getTenantId());

  14.            additionalInfo.put("license", SecurityConstants.PIGX_LICENSE);

  15.            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

  16.            return accessToken;

  17.        };

  18.    }

  • 生成的token 以下,含有關鍵的字段

重寫默認的資源服務器處理行爲

  • 再也不使用RemoteTokenServices 去掉用認證中心 CheckToken,自定義客戶端TokenService

 
 
  1. @Slf4j

  2. public class PigxCustomTokenServices implements ResourceServerTokenServices {

  3.    @Setter

  4.    private TokenStore tokenStore;


  5.    @Setter

  6.    private DefaultAccessTokenConverter defaultAccessTokenConverter;


  7.    @Setter

  8.    private JwtAccessTokenConverter jwtAccessTokenConverter;


  9.    @Override

  10.    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {

  11.        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);

  12.        UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();

  13.        defaultAccessTokenConverter.setUserTokenConverter(userTokenConverter);

  14.        Map<String, ?> map = jwtAccessTokenConverter.convertAccessToken(readAccessToken(accessToken), oAuth2Authentication);

  15.        return defaultAccessTokenConverter.extractAuthentication(map);

  16.    }



  17.    @Override

  18.    public OAuth2AccessToken readAccessToken(String accessToken) {

  19.        return tokenStore.readAccessToken(accessToken);

  20.    }

  21. }

  • 解析jwt 組裝成Authentication

 
 
  1. /**

  2. * @author lengleng

  3. * @date 2019-03-17

  4. * <p>

  5. * jwt 轉化用戶信息

  6. */

  7. public class PigxUserAuthenticationConverter implements UserAuthenticationConverter {

  8.    private static final String USER_ID = "user_id";

  9.    private static final String DEPT_ID = "dept_id";

  10.    private static final String TENANT_ID = "tenant_id";

  11.    private static final String N_A = "N/A";


  12.    @Override

  13.    public Authentication extractAuthentication(Map<String, ?> map) {

  14.        if (map.containsKey(USERNAME)) {

  15.            Collection<? extends GrantedAuthority> authorities = getAuthorities(map);


  16.            String username = (String) map.get(USERNAME);

  17.            Integer id = (Integer) map.get(USER_ID);

  18.            Integer deptId = (Integer) map.get(DEPT_ID);

  19.            Integer tenantId = (Integer) map.get(TENANT_ID);

  20.            PigxUser user = new PigxUser(id, deptId, tenantId, username, N_A, true

  21.                    , true, true, true, authorities);

  22.            return new UsernamePasswordAuthenticationToken(user, N_A, authorities);

  23.        }

  24.        return null;

  25.    }


  26.    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {

  27.        Object authorities = map.get(AUTHORITIES);

  28.        if (authorities instanceof String) {

  29.            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);

  30.        }

  31.        if (authorities instanceof Collection) {

  32.            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils

  33.                    .collectionToCommaDelimitedString((Collection<?>) authorities));

  34.        }

  35.        throw new IllegalArgumentException("Authorities must be either a String or a Collection");

  36.    }

  37. }

  • 資源服務器配置中注入以上配置便可

 
 
  1. @Slf4j

  2. public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

  3.    @Override

  4.    public void configure(ResourceServerSecurityConfigurer resources) {

  5.        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();

  6.        UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();

  7.        accessTokenConverter.setUserTokenConverter(userTokenConverter);


  8.        PigxCustomTokenServices tokenServices = new PigxCustomTokenServices();


  9.        // 這裏的簽名key 保持和認證中心一致

  10.        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

  11.        converter.setSigningKey("123");

  12.        converter.setVerifier(new MacSigner("123"));

  13.        JwtTokenStore jwtTokenStore = new JwtTokenStore(converter);

  14.        tokenServices.setTokenStore(jwtTokenStore);

  15.        tokenServices.setJwtAccessTokenConverter(converter);

  16.        tokenServices.setDefaultAccessTokenConverter(accessTokenConverter);


  17.        resources

  18.                .authenticationEntryPoint(resourceAuthExceptionEntryPoint)

  19.                .tokenServices(tokenServices);

  20.    }

  21. }

使用JWT 擴展後帶來的問題

  • JWT 的最大缺點是,因爲服務器不保存 session 狀態,所以沒法在使用過程當中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期以前就會始終有效,除非服務器部署額外的邏輯。

  • 去認證服務器校驗的過程就是 經過tokenstore 來控制jwt 安全性的一個方法,去掉Check-token 意味着 jwt token 安全性不可保證

  • JWT 自己包含了認證信息,一旦泄露,任何人均可以得到該令牌的全部權限。爲了減小盜用,JWT 的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。

  • 爲了減小盜用,JWT 不該該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

相關文章
相關標籤/搜索