82. 更多细节

82.1 单点登录

[Note] Note

在版本 1.3 中,所有 OAuth2 SSO 和资源服务器功能都移到了 Spring Boot。你可以在 Spring Boot 用户指南中找到文档。

82.2 令牌中继

令牌中继是 OAuth2 使用者充当客户端并将传入令牌转发给传出资源请求的地方。使用者可以是纯客户端(如 SSO 应用程序)或资源服务器。

82.2.1 Spring Cloud Gateway 中的客户端令牌中继

如果你的应用程序也有一个 Spring Cloud Gateway 嵌入的反向代理,那么可以要求它将 OAuth2 访问令牌转发到它所代理的服务的下游。因此,上面的 SSO 应用程序可以这样简单地进行增强:

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

或者这样

application.yaml. 

spring:
  cloud:
    gateway:
      routes:
      - id: resource
        uri: http://localhost:9000
        predicates:
        - Path=/resource
        filters:
        - TokenRelay=

它将(除了登录用户并获取令牌)将身份验证令牌传递给下游的服务(在本例中是 /resource)。

要为 Spring Cloud Gateway 启用此功能,请添加以下依赖项

  • org.springframework.boot:spring-boot-starter-oauth2-client
  • org.springframework.cloud:spring-cloud-starter-security

它是如何工作的?过滤器从当前已验证的用户中提取访问令牌,并将其放入下游请求的请求头中。

有关完整的工作示例,请参阅此项目。

82.2.2 客户端令牌中继

如果你的应用程序是面向 OAuth2 客户端的用户(即已声明 @EnableOAuth2Sso 或 @EnableOAuth2Client),则它在 Spring Boot 的请求范围内具有 OAuth2ClientContext。你可以从此上下文和自动连接的 OAuth2ProtectedResourceDetails 创建自己的 OAuth2RestTemplate,然后上下文将始终将访问令牌转发到下游,并在访问令牌过期时自动刷新它。(这些是 Spring Security 和 Spring Boot 的功能)

[Note] Note

如果你使用 client_credentials 令牌,Spring Boot(1.4.1)不会自动创建 OAuth2ProtectedResourceDetails。在这种情况下,你需要创建自己的 ClientCredentialsResourceDetails,并使用 @ConfigurationProperties("security.oauth2.client") 对其进行配置。

82.2.3 Zuul 代理中的客户端令牌中继

如果你的应用程序还具有 Spring Cloud Zuul 嵌入式反向代理(使用 @EnableZuulProxy),那么可以要求它将 OAuth2 访问令牌转发到其代理的服务下游。因此,上面的 SSO 应用程序可以这样简单地进行增强:

app.groovy. 

@Controller
@EnableOAuth2Sso
@EnableZuulProxy
class Application {

}

它将(除了登录用户并获取令牌)将身份验证令牌传递给下游的 /proxy/* 服务。如果这些服务是用 @EnableResourceServer 实现的,那么它们将在正确的头中获得有效的令牌。

它是如何工作的?@EnableOAuth2Sso 注解引入了 spring-cloud-starter-security(可以在传统应用程序中手动进行),从而触发 ZuulFilter 的一些自动配置,ZuulFilter 本身是被激活的,因为 Zuul 在类路径上(通过 @EnableZuulProxy)。过滤器只是从当前经过身份验证的用户中提取一个访问令牌,并将其放入下游请求的请求头中。

82.2.4 资源服务令牌中继

如果你的应用程序有 @EnableResourceServer,则可能希望将传入令牌中继到下游的其他服务。如果使用 RestTemplate 联系下游服务,那么这只是如何创建具有正确上下文的模板的问题。

如果你的服务使用 UserInfoTokenServices 对传入令牌进行身份验证(即,它使用 security.oauth2.user-info-uri 配置),那么只需使用注入的 OAuth2ClientContext 创建 OAuth2RestTemplate(它将在命中后端代码之前由身份验证过程填充)。同样地(使用 Spring Boot 1.4),可以注入一个 UserInfoRestTemplateFactory,并在你的配置中获取它的 OAuth2RestTemplate。例如:

MyConfiguration.java. 

@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
    return factory.getUserInfoRestTemplate();
}

然后,此 rest 模板将具有身份验证过滤器使用的相同 OAuth2ClientContext(请求范围),因此可以使用它发送具有相同访问令牌的请求。

如果你的应用程序不使用 UserInfoTokenServices,但仍然是一个客户端(即它声明了 @EnableOAuth2Client 或 @EnableOAuth2Sso),那么使用 Spring Security Cloud,用户从 @Autowired @OAuth2Context 创建的任何 OAuth2RestOperations 也将转发令牌。这个特性在默认情况下是作为 MVC 处理程序拦截器实现的,所以它只在 Spring MVC 中工作。如果不使用 MVC,那么可以使用一个自定义过滤器或 AOP 拦截器包装一个 AccessTokenContextRelay 来提供相同的功能。

下面是一个基本示例,展示了在其他地方创建的自动装配 rest 模板的使用("foo.com" 是一个接受与周围应用程序相同令牌的资源服务器):

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

如果你不想转发令牌(这是一个有效的选择,因为你可能想扮演自己的角色,而不是发送令牌的客户端),那么只需要创建自己的 OAuth2Context,而不需要自动装配默认的 OAuth2Context。

如果 OAuth2ClientContext 可用,则 Feign 客户端还将接收使用它的拦截器,因此他们还应该在 RestTemplate 可用的任何位置执行令牌中继。