82. More Detail
Note | |
---|---|
All of the OAuth2 SSO and resource server features moved to Spring Boot in version 1.3. You can find documentation in the Spring Boot user guide . |
A Token Relay is where an OAuth2 consumer acts as a Client and forwards the incoming token to outgoing resource requests. The consumer can be a pure Client (like an SSO application) or a Resource Server.
If your app also has a Spring Cloud Gateway embedded reverse proxy then you can ask it to forward OAuth2 access tokens downstream to the services it is proxying. Thus the SSO app above can be enhanced simply like this:
App.java.
@Autowired private TokenRelayGatewayFilterFactory filterFactory; @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("resource", r -> r.path("/resource") .filters(f -> f.filter(filterFactory.apply())) .uri("http://localhost:9000")) .build(); }
or this
application.yaml.
spring: cloud: gateway: routes: - id: resource uri: http://localhost:9000 predicates: - Path=/resource filters: - TokenRelay=
and it will (in addition to logging the user in and grabbing a token) pass the authentication token downstream to the services (in this case /resource
).
To enable this for Spring Cloud Gateway add the following dependencies
org.springframework.boot:spring-boot-starter-oauth2-client
org.springframework.cloud:spring-cloud-starter-security
How does it work? The filter extracts an access token from the currently authenticated user, and puts it in a request header for the downstream requests.
For a full working sample see this project .
If your app is a user facing OAuth2 client (i.e. has declared @EnableOAuth2Sso
or @EnableOAuth2Client
) then it has an OAuth2ClientContext
in request scope from Spring Boot. You can create your own OAuth2RestTemplate
from this context and an autowired OAuth2ProtectedResourceDetails
, and then the context will always forward the access token downstream, also refreshing the access token automatically if it expires. (These are features of Spring Security and Spring Boot.)
Note | |
---|---|
Spring Boot (1.4.1) does not create an |
If your app also has a Spring Cloud Zuul embedded reverse proxy (using @EnableZuulProxy
) then you can ask it to forward OAuth2 access tokens downstream to the services it is proxying. Thus the SSO app above can be enhanced simply like this:
app.groovy.
@Controller @EnableOAuth2Sso @EnableZuulProxy class Application { }
and it will (in addition to logging the user in and grabbing a token) pass the authentication token downstream to the /proxy/*
services. If those services are implemented with @EnableResourceServer
then they will get a valid token in the correct header.
How does it work? The @EnableOAuth2Sso
annotation pulls in spring-cloud-starter-security
(which you could do manually in a traditional app), and that in turn triggers some autoconfiguration for a ZuulFilter
, which itself is activated because Zuul is on the classpath (via @EnableZuulProxy
). The filter just extracts an access token from the currently authenticated user, and puts it in a request header for the downstream requests.
If your app has @EnableResourceServer
you might want to relay the incoming token downstream to other services. If you use a RestTemplate
to contact the downstream services then this is just a matter of how to create the template with the right context.
If your service uses UserInfoTokenServices
to authenticate incoming tokens (i.e. it is using the security.oauth2.user-info-uri
configuration), then you can simply create an OAuth2RestTemplate
using an autowired OAuth2ClientContext
(it will be populated by the authentication process before it hits the backend code). Equivalently (with Spring Boot 1.4), you could inject a UserInfoRestTemplateFactory
and grab its OAuth2RestTemplate
in your configuration. For example:
MyConfiguration.java.
@Bean public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) { return factory.getUserInfoRestTemplate(); }
This rest template will then have the same OAuth2ClientContext
(request-scoped) that is used by the authentication filter, so you can use it to send requests with the same access token.
If your app is not using UserInfoTokenServices
but is still a client (i.e. it declares @EnableOAuth2Client
or @EnableOAuth2Sso
), then with Spring Security Cloud any OAuth2RestOperations
that the user creates from an @Autowired
@OAuth2Context
will also forward tokens. This feature is implemented by default as an MVC handler interceptor, so it only works in Spring MVC. If you are not using MVC you could use a custom filter or AOP interceptor wrapping an AccessTokenContextRelay
to provide the same feature.
Here’s a basic example showing the use of an autowired rest template created elsewhere ("foo.com" is a Resource Server accepting the same tokens as the surrounding app):
MyController.java.
@Autowired private OAuth2RestOperations restTemplate; @RequestMapping("/relay") public String relay() { ResponseEntity<String> response = restTemplate.getForEntity("https://foo.com/bar", String.class); return "Success! (" + response.getBody() + ")"; }
If you don’t want to forward tokens (and that is a valid choice, since you might want to act as yourself, rather than the client that sent you the token), then you only need to create your own OAuth2Context
instead of autowiring the default one.
Feign clients will also pick up an interceptor that uses the OAuth2ClientContext
if it is available, so they should also do a token relay anywhere where a RestTemplate
would.