Spring Boot - Using JWT, OAuth, and Separate Resource and Auth Servers

故事扮演 提交于 2019-12-22 08:34:12

问题


I am attempting to build a Spring application that uses JWT tokens and the OAuth2 protocol. I have the Authentication Server running thanks to this tutorial. However, I am struggling with getting the Resource Server to function properly. From following the article, and thanks to a response to a prior question, this is my current attempt:

Security config for Resource Server:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer clientID;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(signingKey);
        return converter;
    }

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

    @Bean ResourceServerTokenServices tokenService() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        OAuth2AuthenticationManager authManager = new OAuth2AuthenticationManager();
        authManager.setTokenServices(tokenService());
        return authManager;
    }

}

Resource Server config:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private ResourceServerTokenServices tokenServices;

@Value("${security.jwt.resource-ids}")
private String resourceIds;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId(resourceIds).tokenServices(tokenServices);
}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.requestMatchers().and().authorizeRequests().antMatchers("/actuator/**", "/api-docs/**").permitAll()
            .antMatchers("/**").authenticated();
}

}

Security config for Authorization Server (from noted tutorial):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new ShaPasswordEncoder(encodingStrength));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic()
                .realmName(securityRealm)
                .and()
                .csrf()
                .disable();

    }

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

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

    @Bean
    @Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

Now, when I attempt to make a request to the Resource server, I receive an error such as follows:

{"error":"invalid_token","error_description":"Invalid access token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXh wIjoxNTE1MTE3NTU4LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF"}

I have a couple questions:

  • To my understanding, the issue is often associated with token stores. How does one handle separting the servers when a JwtTokenStore is used?
  • Second, currently, my Resource app relies on having access to a key. To my understanding of the JWT and 0Auth specs, this should not be necessary. Rather, I should be able to delegate the validation to the Authentication server itself. From the Spring docs, I thought the following property might be applicable security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token. However, if I attempt to not rely on a key with my Resource Server, then I run into difficulties setting my ResourceServerTokenService. The service expects a token store, the JWTTokenStore uses a JwtAccessTokenConverter, and the converter uses a key (removing the key resulted in the same invalid token error I experienced earlier).

I am really struggling to find articles that show how to separate the Auth and Resource server. Any advice would be appreciated.

EDIT: Actually, the code for the Resource Server is now failing to compile with the following message:

Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key


回答1:


I tried spring oauth and I came across the same error :

Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key

My mistake was that my public certificate was :

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
tadadada
-----END CERTIFICATE-----

And this is not allowed. REMOVE the certificate, just let the public key in this file :

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----

And the startup error will disapear.

For your second question, that's what I understand :

  • The authentication server give you an encrypted token (encrypted with the private key), that contains ALL the permissions of your user.

  • The resource server decrypts the token with the public key, and assumes that the permissions contained in the token are TRUE.

Hopes this help.




回答2:


I ran into this issue, when my public key was formatted as below:

"-----BEGIN RSA PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END RSA PUBLIC KEY-----\n"

When I changed this to:

"-----BEGIN PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END PUBLIC KEY-----\n"

The key was accepted.



来源:https://stackoverflow.com/questions/48098186/spring-boot-using-jwt-oauth-and-separate-resource-and-auth-servers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!