Spring OAuth 2 + JWT 在访问令牌中包含附加信息 [英] Spring OAuth 2 + JWT Inlcuding additional info JUST in access token

查看:21
本文介绍了Spring OAuth 2 + JWT 在访问令牌中包含附加信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以在实现我自己的 TokenEnhancer 的访问令牌中包含其他信息,但此类信息包含两次.一个在编码的 access_token 中,另一个在身份验证服务器响应中.

I am able to include additional information into the access token implementing my own TokenEnhancer, but such info is included twice. One in the encoded access_token, and the other in the auth server response.

长话短说!我请求具有正确凭据的访问令牌,并得到以下响应:

Long story short! I request an access token with right credentials, and I get this response:

{
    "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/

So I can see clientId included in the response... Now I copy my access_token and I decoded in: https://jwt.io/

并且在有效载荷中还包括 clientId...

And in the payload is included also the clientId...

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

My question is: How can I remove the addional info from the server response and leave it just in the tokens (access_token and 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:

And my 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 进行编码,并在响应中包含附加信息.这个想法是在 access_token 中编码后初始化 DefaultOAuth2AccessToken.additionalInformation.解决办法是:

After a while I figured it out. JwtAccessTokenConverter implements TokenEnhaner too. First CustomTokenEnhaner.enhance is called, including the additional information. Then JwtAccessTokenConverter.enhance, encoding the AccessToken by CustomTokenEnhaner.enhance and including addional information to the response. The idea is initialize DefaultOAuth2AccessToken.additionalInformation once is encoded in the access_token. Solution is:

首先让CustomTokenEnhancer扩展JwtAccessTokenConverter,覆盖enhance,附加附加信息,调用父级enhance初始化DefaultOAuth2AccessToken.additionalInformation:

First let CustomTokenEnhancer extends JwtAccessTokenConverter, override enhance, attach additional information, call enhance from the parent and initialize the 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

And last step, would be delete the bean

@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

And add the key to the CustomTokenEnhancer

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

就是这样.

这篇关于Spring OAuth 2 + JWT 在访问令牌中包含附加信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆