简体   繁体   中英

Microservices and Spring Security OAuth2

I already have a OAuth2 authorization server running in another project. Now I would need to secure several simple spring-boot rest-servers with OAuth2. But I'm finding the Spring documentation really really limited when it comes to separating Authorization and Resource servers.

I've also found several questions where the answer has been "Well they can be different boxes as long as they share the same tokenStore datasource". Can this really be true? How could this ever work for microservices? It would seem like a really odd thing that every rest service would need to implement it's own OAuth authorization server.

So how do I setup Oauth2.0 security for spring-boot rest-endpoints that refer to a remote oauth authorization server (possibly not even written with Spring)?

There's this thing called RemoteTokenServices that seems promising but it's not really documented at all.

While configuring your auh server::

Create a new clientDetails in ClientDetailsServiceConfigurer for resource server. which will be used to configure RemoteTokenService .

Configure Spring Security OAuth2 in your resource server:

Create a class which is annotate with @EnableWebSecurity , @Configuration and extends WebSecurityConfigurerAdapter .

@Configuration
@EnableWebSecurity
protected static class ResourceConfiguration extends WebSecurityConfigurerAdapter {
  // methods        
}

Create a method with @Bean annotated which will return instance of TokenService , which will be used to create AuthenticationManager .

In this method create an instance of RemoteTokenService and set clientId, client_secret , checkTokenEndpointUrl and DefaultAccessTokenConverterWithClientRoles (this class is our implementation to get client_authority while authenticating accessToken in OAuth2 server.)

@Bean
public ResourceServerTokenServices tokenService() {
    RemoteTokenServices tokenServices = new RemoteTokenServices();
    tokenServices.setClientId("resource_id");
    tokenServices.setClientSecret("resource_secret");
    tokenServices.setCheckTokenEndpointUrl("http://<server-url>: <port>/oauth/check_token");
    return tokenServices;
}

Override authenticationManagerBean() method and annotate it with @Bean and return an instance of OAuth2AuthenticationManager with TokenService injected.

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
    authenticationManager.setTokenServices(tokenService());
    return authenticationManager;
}

Create a class annotated with @EnableResourceServer , @Configuration and extend ResourceServerConfigurerAdapter .

@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
  // Mehotds
}

Override Configure methods form the super class to configure resource server. Different configurer to configure Resource server.

ResourceServerSecurityConfigurer : to configure Resource_id.

HttpSecurity : This will configure security filter to tell it that user requires authentication for protected URLs (APIs).

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

@Override
public void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
     .authorizeRequests()
     .antMatchers("/**").authenticated()
     .and()
     .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    // @formatter:on
}

.antMatcher("/**").authenticated() this line will secure every api url of your resource server. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) will not create session.

PS:: If any thing is wrong then tell me.

It's not really true that both have to share the same database, provided the resource server has some way of validating that the access token is genuine (was issued by the authorizations server) and also some way of decoding it (it may need to know what scopes it grants, for example). The OAuth2 spec doesn't say anything about how this is to be achieved.

If the resource is in the same process as the authorization server, then sharing the data is an easy option. Otherwise, it needs some way of translating the token. If the token is just an opaque string of random bytes, then obviously it has to exchange it for the real information. This is what RemoteTokenServices does. The authorization server exposes a /check_token endpoint to allow the tokens to be decoded.

An alternative is to actually encode the info in the token and have the authorization server digitally sign it. The resource server can then decode and validate the token itself as long as it understand the format.

I'd recommend you look at the Cloudfoundry UAA which provides an authorization server out of the box which is implemented using signed JWT tokens (it also exposes the /check_token endpoint). The Tokens overview and the API docs are probably a good starting point.

A working sample of RemoteTokenService can be found here .

For more details about check_token api, you can refer org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint.java

On the resource server end, OAuth2AuthenticationProcessingFilter validates OAuth2 token by calling OAuth2AuthenticationManager.authenticate() method which makes the call to RemoteTokenServices.loadAuthentication() to validate the token from the Auth Server.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM