如何在 Spring Boot 中从资源服务器中的令牌中提取声明

Posted

技术标签:

【中文标题】如何在 Spring Boot 中从资源服务器中的令牌中提取声明【英文标题】:How to extract claims from token in Resource Server, in Spring Boot 【发布时间】:2020-05-18 22:28:24 【问题描述】:

我的身份验证服务器配置为根据我的数据库上的表检索检查凭据,并使用令牌增强器传递其他声明 - 访问控制相关内容。

因此,我是这样写的:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 
    @Value("$security.signing-key")
    private String signingKey;
    private @Autowired TokenStore tokenStore;
    private @Autowired AuthenticationManager authenticationManager;
    private @Autowired CustomUserDetailsService userDetailsService;

    private @Autowired JwtAccessTokenConverter accessTokenConverter;

    private static final Logger LOGGER = LogManager.getLogger(AuthorizationServerConfig.class);

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource oauthDataSource() 
        DataSource ds = null;
        try 
            Context initialContex = new InitialContext();
            ds = (DataSource) (initialContex.lookup("java:/jdbc/oauthdatasource"));
            if (ds != null) 
                ds.getConnection();
            
         catch (NamingException ex) 
            LOGGER.error("Naming exception thrown: ", ex);
         catch (SQLException ex) 
            LOGGER.info("SQL exception thrown: ", ex);
        
        return ds;
    

    @Bean
    public JdbcClientDetailsService clientDetailsServices() 
        return new JdbcClientDetailsService(oauthDataSource());
    

    @Bean
    public TokenStore tokenStore() 
        return new CustomJdbcTokenStore(oauthDataSource());
    

    @Bean
    public ApprovalStore approvalStore() 
        return new JdbcApprovalStore(oauthDataSource());
    

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() 
        return new JdbcAuthorizationCodeServices(oauthDataSource());
    

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() 
        CustomTokenEnhancer converter = new CustomTokenEnhancer();
        converter.setSigningKey(signingKey);
        return converter;
    

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        clients.withClientDetails(clientDetailsServices());
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .accessTokenConverter(accessTokenConverter)
                .userDetailsService(userDetailsService)
                .reuseRefreshTokens(false);
    

这很好用。当我通过 POSTMAN 拨打电话时,我得到如下信息:


    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2hhcmVwb3J0YWwiXSwiaW5mb19maXJzdCI6IlRoaXMgaXMgdGhlIGZpcnN0IEluZm8iLCJ1c2VyX25hbWUiOiJBdXRoZW50aWNhdGlvbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSIsInRydXN0Il0sImluZm9fc2Vjb25kIjoiVGhpcyBpcyB0aGUgc2Vjb25kIGluZm8iLCJleHAiOjE1ODA3MTMyOTQsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI1MTg4MGJhZC00MGJiLTQ3ZTItODRjZS1lNDUyNGY1Y2Y3MzciLCJjbGllbnRfaWQiOiJzaGFyZXBvcnRhbC1jbGllbnQifQ.ABmBjwmVDb2acZtGSQrjKcCwfZwhw4R_rpW4y5JA1jY",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2hhcmVwb3J0YWwiXSwiaW5mb19maXJzdCI6IlRoaXMgaXMgdGhlIGZpcnN0IEluZm8iLCJ1c2VyX25hbWUiOiJBdXRoZW50aWNhdGlvbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSIsInRydXN0Il0sImF0aSI6IjUxODgwYmFkLTQwYmItNDdlMi04NGNlLWU0NTI0ZjVjZjczNyIsImluZm9fc2Vjb25kIjoiVGhpcyBpcyB0aGUgc2Vjb25kIGluZm8iLCJleHAiOjE1ODA3MTM0MzQsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiIyZDYxMDU2ZC01ZDMwLTRhZTQtOWMxZC0zZjliYjRiOWYxOGIiLCJjbGllbnRfaWQiOiJzaGFyZXBvcnRhbC1jbGllbnQifQ.qSLpJm4QxZTIVn1WYWH7EFBS8ryjF1hsD6RSRrEBZd0",
    "expires_in": 359,
    "scope": "read write trust"

现在的问题是我的资源服务器。这是在我向身份验证服务器添加令牌增强器之前的样子:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter 
    private @Autowired CustomAuthenticationEntryPoint entryPoint;
    private @Autowired TokenStore tokenStore;
    private static final String RESOURCE_ID = "resourceid";

    private static final Logger LOGGER = LogManager.getLogger(ResourceServerConfig.class);

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource oauthDataSource() 
        DataSource ds = null;
        try 
            Context initialContex = new InitialContext();
            ds = (DataSource) (initialContex.lookup("java:/jdbc/oauthdatasource"));
            if (ds != null) 
                ds.getConnection();
            
         catch (NamingException ex) 
            LOGGER.error("Naming exception thrown: ", ex);
         catch (SQLException ex) 
            LOGGER.info("SQL exception thrown: ", ex);
        
        return ds;
    

    @Bean
    public TokenStore getTokenStore() 
        return new JdbcTokenStore(oauthDataSource());
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
                .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")
                .and()
                .headers().addHeaderWriter((request, response) -> 
                    response.addHeader("Access-Control-Allow-Origin", "*");
                    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
                    response.setHeader("Access-Control-Max-Age", "3600");
                    response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
                    if (request.getMethod().equals("OPTIONS")) 
                        response.setStatus(HttpServletResponse.SC_OK);
                    
                )
                .and().exceptionHandling().authenticationEntryPoint(entryPoint);
    

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
        resources.resourceId(RESOURCE_ID).tokenStore(tokenStore).authenticationEntryPoint(entryPoint);
    

我希望通过身份验证服务器检索我作为附加声明放置的访问控制信息,但我不知道如何去做。

我在互联网上看到了几个例子,包括:How to extract claims from Spring Security OAuht2 Boot in the Resource Server?,但没有一个对我有用。或者也许我错过了什么。

请问,我必须添加什么才能使这成为可能?

【问题讨论】:

你可能想看看这个答案:***.com/questions/46333945/… @filip 该死的,我希望我在遇到这个问题时能看到这个。本来可以帮我减轻很多压力。但是,我将实施以供将来使用。谢谢 【参考方案1】:

我不得不使用第三方库来实现这一点。

这是图书馆的链接:https://github.com/auth0/java-jwt

效果很好。

在我的资源服务器中,我可以获取我的令牌值,然后使用 java-jwt 库,我可以提取我在授权服务器中设置的任何声明:

public Map<String, Claim> getClaims() 
        Map<String, Claim> claims = new HashMap<>();

        String tokenValue = ((OAuth2AuthenticationDetails)((OAuth2Authentication) authenticationFacade.getAuthentication()).getDetails()).getTokenValue();
        try 
            DecodedJWT jwt = JWT.decode(tokenValue);
            claims = jwt.getClaims();
         catch (JWTDecodeException ex) 
            LOGGER.info("Error decoding token value");
            LOGGER.error("Error decoding token value", ex);
        

        return claims;

您应该查看 java-jwt 的文档以了解更多信息。

【讨论】:

以上是关于如何在 Spring Boot 中从资源服务器中的令牌中提取声明的主要内容,如果未能解决你的问题,请参考以下文章