Spring OAuth 2 + JWT包含访问令牌中的附加信息

Spring OAuth 2 + JWT包含访问令牌中的附加信息

问题描述:

我能够将附加信息包含到实现我自己的令牌增强器的访问令牌中,但这些信息包含两次。一个在编码的access_token中,另一个在auth服务器响应中。Spring OAuth 2 + JWT包含访问令牌中的附加信息

长话短说!我请求与正确的凭据的访问令牌,我得到这样的响应:

{ 
    "access_token" : "eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRJZCI6Ik1ZX0NVU1RPTV9JTkZPX0NMSUVOVCIsInVzZXJfbmFtZSI6IlVTRVIiLCJzY29wZSI6WyJGT08iXSwiZXhwIjoxNTA2MzkwOTM5LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiZjJkYWFkM2ItYzkzOC00ZjExLWI3ODctMzExZDdlNjYzYzhhIiwiY2xpZW50X2lkIjoid2ViX2FwcCJ9.IdgYRxwZGRPR97nxHpAcJXNWDTShQE1tsg9NsBwlOk8eDWE1B-mjfGTaKiyTO1-m9GBpXnxt2PaOV7AbdLsCZ5xLPUR0_5ehuNB6WCXLSkdac5xbw-rmNdJHTe9gLJizOZAKF6J-_Xo9OOQISKBqliY5vo5y0btqIw4CX6-ukYoWZmwHThwnAsEA_PqGuEXsbXMGz-vqJaSVpvJeEOBNL0KOh-cNxc0ft-rJ3snjPerN_efAiZdFkzxdCeuoGmZvSyHRjYR8kQ3ZqZ5MOunw9YuTvidL1IK5TODHQ2BjiCTpbgDlYx-Oh5UxcYNrPOhD-tBjRuuqDSz8K6ddpke4RQ", 
    "token_type" : "bearer", 
    "refresh_token" : "eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRJZCI6Ik1ZX0NVU1RPTV9JTkZPX0NMSUVOVCIsInVzZXJfbmFtZSI6IlVTRVIiLCJzY29wZSI6WyJGT08iXSwiYXRpIjoiZjJkYWFkM2ItYzkzOC00ZjExLWI3ODctMzExZDdlNjYzYzhhIiwiZXhwIjoxNTA4OTM5NzM5LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOGU2Zjc0OTEtMmQ3MC00NTUwLThhMDgtZjk0YjkzYTVkYWZmIiwiY2xpZW50X2lkIjoid2ViX2FwcCJ9.MqwMrYrofu7pUQu2mF33__h6M4OWSRrQ-lc8JzTn0DkpJ6a3-yjnjjppZ9fs3KBz_lpRIO8jo--eId449rEjP4M3_9lDRSW9_HyBAvd57OtyUHa5SPM9prD6ReXGCyiIw2gO07euIf-Vp4UHsjoKK0MdtfMmFIWms1JMGFBmzBha8kqKaMxKzppGy-jVdP7384K9oovD20H-NubjScfoO2Crp1cTM-SXc-0v6kwB1qV-cI6HKXmbkoFhbH2bL_nRvXTkLYI-UvRNTNLHzqhcqztLTrszcWa2BjNU2IofsNByFS8BHTDV1vu0BqZA4kfNCJcFJ89tBDt2L8vfFkYezQ", 
    "expires_in" : 43199, 
    "scope" : "FOO", 
    "clientId" : "MY_CUSTOM_INFO_CLIENT", 
    "jti" : "f2daad3b-c938-4f11-b787-311d7e663c8a" 
} 

所以我能看到的clientId包括在响应......现在,我复制我和的access_token我解码:https://jwt.io/

而且在有效载荷也包括clientId ...

我的问题是:如何从服务器响应中删除附加信息,并将它留在令牌(access_token和refresh_token)中。

请参阅下面的代码:

import java.util.Arrays; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Primary; 
import org.springframework.core.io.ClassPathResource; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 
import org.springframework.security.oauth2.provider.token.DefaultTokenServices; 
import org.springframework.security.oauth2.provider.token.TokenEnhancer; 
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; 
import org.springframework.security.oauth2.provider.token.TokenStore; 
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; 
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; 

@Configuration 
@EnableAuthorizationServer 
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 

    @Autowired 
    @Qualifier("authenticationManagerBean") 
    private AuthenticationManager authenticationManager; 

    @Override 
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
     TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 
     tokenEnhancerChain.setTokenEnhancers(
        Arrays.asList(tokenEnhancer(), accessTokenConverter())); 
     endpoints 
       .tokenStore(tokenStore()) 
       .authenticationManager(authenticationManager) 
       .tokenEnhancer(tokenEnhancerChain); 
    } 
    @Override 
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 

     clients.inMemory() 
      .withClient("web_app") 
      .secret("web_app123") 
      .scopes("FOO") 
      .autoApprove(true) 
      .authorities("FOO_READ", "FOO_WRITE") 
      .authorizedGrantTypes("refresh_token", "password"); 
} 
    @Bean 
    public TokenStore tokenStore() { 
     return new JwtTokenStore(accessTokenConverter()); 
    } 

    @Bean 
    public JwtAccessTokenConverter accessTokenConverter() { 
     JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 
     KeyStoreKeyFactory keyStoreKeyFactory = 
      new KeyStoreKeyFactory(new ClassPathResource("mykey.jks"), "mykey123".toCharArray()); 
     converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey")); 
     return converter; 
    } 

    @Bean 
    @Primary 
    public DefaultTokenServices tokenServices() { 
     DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); 
     defaultTokenServices.setTokenStore(tokenStore()); 
     defaultTokenServices.setSupportRefreshToken(true); 
     return defaultTokenServices; 
    } 

    @Bean 
    public TokenEnhancer tokenEnhancer() { 
     return new CustomTokenEnhancer(); 
    } 
} 

而且我CustomTokenEnhancer:

import java.util.HashMap; 
import java.util.Map; 

import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 
import org.springframework.security.oauth2.common.OAuth2AccessToken; 
import org.springframework.security.oauth2.provider.OAuth2Authentication; 
import org.springframework.security.oauth2.provider.token.TokenEnhancer; 

import com.mapflow.ms.security.service.UserDetailInfo; 

public class CustomTokenEnhancer implements TokenEnhancer { 

    @Override 
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, 
      OAuth2Authentication authentication) { 
     UserDetailInfo user = (UserDetailInfo) authentication.getPrincipal(); 
     final Map<String, Object> additionalInfo = new HashMap<String, Object>(); 

     additionalInfo.put("clientId", user.getClientId()); 

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

     return accessToken; 
    } 
} 

一段时间,我想通了之后。 JwtAccessTokenConverter也实现了TokenEnhaner。首先调用CustomTokenEnhaner.enhance,包括附加信息。然后JwtAccessTokenConverter.enhance,通过CustomTokenEnhaner.enhance对AccessToken进行编码,并包括响应的附加信息。该想法初始化为DefaultOAuth2AccessToken.additionalInformation,一次编码在access_token中。解决办法是:

首先让CustomTokenEnhancer延伸JwtAccessTokenConverter,覆盖enhance,附加额外的信息,请从父enhance并初始化DefaultOAuth2AccessToken.additionalInformation

public class CustomTokenConverter extends JwtAccessTokenConverter { 

    @Override 
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, 
      OAuth2Authentication authentication) { 
     if(authentication.getOAuth2Request().getGrantType().equalsIgnoreCase("password")) { 
      UserDetailInfo user = (UserDetailInfo) authentication.getPrincipal(); 
      final Map<String, Object> additionalInfo = new HashMap<String, Object>(); 

      additionalInfo.put("clientId", user.getClientId()); 

      ((DefaultOAuth2AccessToken) accessToken) 
        .setAdditionalInformation(additionalInfo);  
     } 
     accessToken = super.enhance(accessToken, authentication); 
     ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(new HashMap<>()); 
     return accessToken; 
    } 
} 

而最后一步,将删除豆

@Bean 
    public JwtAccessTokenConverter accessTokenConverter() { 
     JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 
     KeyStoreKeyFactory keyStoreKeyFactory = 
      new KeyStoreKeyFactory(new ClassPathResource("mykey.jks"), "mykey123".toCharArray()); 
     converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey")); 
     return converter; 
    } 

并将该密钥添加到CustomTokenEnhancer

@Bean 
public JwtAccessTokenConverter accessTokenConverter() { 
    CustomTokenConverter tokenConverter = new CustomTokenConverter(); 
    tokenConverter.setSigningKey("PswMapview2017"); 
    return tokenConverter; 
} 

就是这样。