How to configure Resource Server in Spring Securit

2020-06-12 04:35发布

I have an oauth2 jwt token server configured to set additional info about the user authorities.

@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;
    }

}

I am not sure how to configure my resource server to extract the user authorities set by the oauth2 server and use that authority to be used for @Secured annotated controllers in Spring Security framework.

My Auth server configuration looks like this:

@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");
    }
}

And my Resource Server configuration looks like this:

@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());
    }

}

My test case is failing miserably saying:

{"error":"invalid_token","error_description":"Cannot convert access token to JSON"}

How do I get the Authentication object out of the JWT? How do I authenticate the client, with client credentials? How do I use @Secured annotation on my resource controllers?

What code is used on the resource server side to decode the token in order to extract client credentials and what code gets to user role verified?

Please help, as I already spent 2 days banging my head on this seemingly easy task.

Note: I receive the token from Auth server as: {access_token=b5d89a13-3c8b-4bda-b0f2-a6e9d7b7a285, token_type=bearer, refresh_token=43777224-b6f2-44d7-bf36-4e1934d32cbb, expires_in=43199, scope=read write trust, authorities=[{authority=ROLE_USER}, {authority=ROLE_ADMIN}]}

Please explain the concepts and point out if anything is missing from my configuration. I need to know the best practices in configuring my resource and auth server please.

1条回答
叛逆
2楼-- · 2020-06-12 05:14

In the following I'm referring to this Baeldung tutorial that I already implemented successfully: http://www.baeldung.com/spring-security-oauth-jwt

First at all: The CustomTokenEnhancer is used on the AuthorizationServer side to enhance a created token with additional custom information. You should use the so called DefaultAccessTokenConverter on the ResourceServer side to extract these extra claims.

You can @Autowire the CustomAccessTokenConverter into your ResourceServerConfiguration class and then set it to your JwtTokenStore() configuration.

ResourceServerConfiguration:

@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;
}

The CustomAccessTokenConverter can be configured, so that the custom claims get extracted here.

CustomAccessTokenConverter:

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {

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

}

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

查看更多
登录 后发表回答