如何在 Spring Security 中配置资源服务器以使用 JWT 令牌中的附加信息

Posted

技术标签:

【中文标题】如何在 Spring Security 中配置资源服务器以使用 JWT 令牌中的附加信息【英文标题】:How to configure Resource Server in Spring Security for it to use additional information in JWT token 【发布时间】:2016-08-29 20:20:11 【问题描述】:

我有一个配置为设置有关用户权限的附加信息的 oauth2 jwt 令牌服务器。

@Configuration
@Component
public class CustomTokenEnhancer extends JwtAccessTokenConverter 

    CustomTokenEnhancer()
        super();
    

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) 
        // TODO Auto-generated method stub
        MyUserDetails user = (MyUserDetails) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();
        @SuppressWarnings("unchecked")
        List<GrantedAuthority> authorities= (List<GrantedAuthority>) user.getAuthorities();
        additionalInfo.put("authorities", authorities);

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

        return accessToken;
    


我不确定如何配置我的资源服务器以提取由 oauth2 服务器设置的用户权限,并将该权限用于 Spring Security 框架中的 @Secured 注释控制器。

我的 Auth 服务器配置如下所示:

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter 

    @Value("$config.oauth2.privateKey")
    private String privateKey;

    @Value("$config.oauth2.publicKey")
    private String publicKey;

    @Value("config.clienturl")
    private String clientUrl;

    @Autowired
    AuthenticationManager authenticationManager;

    @Bean
    public JwtAccessTokenConverter customTokenEnhancer()

        JwtAccessTokenConverter customTokenEnhancer = new CustomTokenEnhancer();
        customTokenEnhancer.setSigningKey(privateKey);

        return customTokenEnhancer;
    

    @Bean
    public JwtTokenStore tokenStore() 
        return new JwtTokenStore(customTokenEnhancer());
    


    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
        oauthServer
                .tokenKeyAccess("isAnonymous() || hasRole('ROLE_TRUSTED_CLIENT')") // permitAll()
                .checkTokenAccess("hasRole('TRUSTED_CLIENT')"); // isAuthenticated()
    


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints


        .authenticationManager(authenticationManager)
        .tokenStore(tokenStore())
        .accessTokenConverter(customTokenEnhancer())
;
    

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 

        String url = clientUrl;

        clients.inMemory()


        .withClient("public") 
        .authorizedGrantTypes("client_credentials", "implicit")
        .scopes("read")
        .redirectUris(url)

        .and()


        .withClient("eagree_web").secret("eagree_web_dev")
        //eagree_web should come from properties file?
        .authorities("ROLE_TRUSTED_CLIENT") 
        .authorizedGrantTypes("client_credentials", "password", "authorization_code", "refresh_token")
        .scopes("read", "write", "trust") 
        .redirectUris(url).resourceIds("dummy");
    

我的资源服务器配置如下:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration  extends ResourceServerConfigurerAdapter 



    @Value("config.oauth2.publicKey")
    private String publicKey;

    @Autowired
    CustomTokenEnhancer tokenConverter;

    @Autowired
    JwtTokenStore jwtTokenStore;

    @Bean
    public JwtTokenStore jwtTokenStore() 
        tokenConverter.setVerifierKey(publicKey);
        jwtTokenStore.setTokenEnhancer(tokenConverter);
        return jwtTokenStore;
    

    @Bean
    public ResourceServerTokenServices defaultTokenServices() 
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenEnhancer(tokenConverter);
        defaultTokenServices.setTokenStore(jwtTokenStore());
        return defaultTokenServices;
    


    @Override
    public void configure(HttpSecurity http) throws Exception 
        super.configure(http);
        // @formatter:off
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and()
                .requestMatchers()
                .antMatchers("/**")
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
                .antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
                .antMatchers(HttpMethod.PATCH, "/api/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PUT, "/api/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.DELETE, "/api/**").access("#oauth2.hasScope('write')")
                .antMatchers("/admin/**").access("hasRole('ROLE_USER')");

        // @formatter:on
    

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
        System.out.println("Configuring ResourceServerSecurityConfigurer ");
        resources.resourceId("dummy").tokenServices(defaultTokenServices());
    


我的测试用例失败得很惨:

"error":"invalid_token","error_description":"无法将访问令牌转换为 JSON"

如何从 JWT 中获取 Authentication 对象? 如何使用客户端凭据对客户端进行身份验证? 如何在我的资源控制器上使用 @Secured 注解?

资源服务器端使用什么代码按顺序解码令牌 提取客户端凭据以及验证用户角色的代码是什么?

请帮忙,因为我已经花了 2 天的时间来解决这个问题 简单的任务。

注意:我从 Auth 服务器接收令牌: access_token=b5d89a13-3c8b-4bda-b0f2-a6e9d7b7a285, token_type=bearer, refresh_token=43777224-b6f2-44d7-bf36-4e1934d32cbb, expires_in=43199, scope=read write trust, authority=[authority=ROLE_USER, authority =ROLE_ADMIN]

请解释这些概念并指出我的配置中是否缺少任何内容。我需要知道配置资源和身份验证服务器的最佳做法。

【问题讨论】:

【参考方案1】:

下面我指的是我已经成功实现的这个 Baeldung 教程:http://www.baeldung.com/spring-security-oauth-jwt

首先:在 AuthorizationServer 端使用 CustomTokenEnhancer 来使用额外的自定义信息来增强创建的令牌。您应该在 ResourceServer 端使用所谓的 DefaultAccessTokenConverter 来提取这些额外的声明。

您可以将 @Autowire CustomAccessTokenConverter 放入您的 ResourceServerConfiguration 类,然后将其设置为您的 JwtTokenStore() 配置。

资源服务器配置:

@Autowired
private CustomAccessTokenConverter yourCustomAccessTokenConverter;

@Bean
public TokenStore tokenStore() 
    return new JwtTokenStore(accessTokenConverter());


@Bean
public JwtAccessTokenConverter accessTokenConverter() 
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setAccessTokenConverter(yourCustomAccessTokenConverter);
    converter.setSigningKey(yourSigningKey);
    return converter;

可以配置 CustomAccessTokenConverter,以便在此处提取自定义声明。

CustomAccessTokenConverter:

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter 

    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> claims) 
        OAuth2Authentication authentication = super.extractAuthentication(claims);
        authentication.setDetails(claims);
        return authentication;
    


(见:https://github.com/Baeldung/spring-security-oauth/blob/master/oauth-resource-server-1/src/main/java/org/baeldung/config/CustomAccessTokenConverter.java)

【讨论】:

以上是关于如何在 Spring Security 中配置资源服务器以使用 JWT 令牌中的附加信息的主要内容,如果未能解决你的问题,请参考以下文章

如何让spring security不拦截第三方的对接方法

Spring Security实现OAuth2.0——资源服务

spring boot整合security 4,怎么设置忽略的静态资源?

spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)

Spring Security中登录后如何访问资源?

Spring Security之动态配置资源权限