你现在可能想知道,我们是否可以在一个基于 OAuth 2 框架设计的系统中使用响应式应用程序。在本文中,我们将讨论如何将资源服务器实现为响应式应用程序。您将学习如何配置响应式应用程序,使其依赖于通过 OAuth 2 实现的身份认证方法。由于现在使用 OAuth 2 非常普遍,您可能会遇到需要将资源服务器应用程序设计为响应式服务器的需求。我创建了一个新项目,我们将实现一个响应式资源服务器应用程序。您需要在 pom.xml 中添加依赖项,如下面的代码片段所示:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
我们需要一个端点来测试应用程序,因此我们添加了一个控制器类。下一个代码片段展示了控制器类:
@RestController public class HelloController { @GetMapping("/hello") public Mono<String> hello() { return Mono.just("Hello!"); } }
现在,示例中最重要的部分:安全配置。对于本例,我们将资源服务器配置为使用授权服务器公开的公钥进行令牌签名验证。这种方法与《动手操作:一个 OAuth 2 应用程序(2) - 配置 Keycloak 为授权服务器》中使用 Keycloak 作为授权服务器的方法相同。在这个示例中,我实际上使用了相同的配置服务器。您可以选择执行相同的操作,也可以实现自定义授权服务器,如《OAuth 2:实现授权服务器》所述。
要配置身份认证方法,我们使用 SecurityWebFilterChain,如 《响应式 APP 使用 Spring Security(3)》所述。但是我们没有使用 httpBasic() 方法,而是调用 oauth2ResourceServer() 方法。然后,通过调用 jwt() 方法,我们定义了使用的令牌类型,并且通过使用 Customizer 对象,我们指定了验证令牌签名的方式。在清单 12 中,您可以找到配置类的定义。
清单 12 配置类
@Configuration public class ProjectConfig { @Value("${jwk.endpoint}") private String jwkEndpoint; @Bean public SecurityWebFilterChain securityWebFilterChain( ServerHttpSecurity http) { return http.authorizeExchange() .anyExchange().authenticated() .and().oauth2ResourceServer() //配置资源服务器认证方式 .jwt(jwtSpec -> { //指定验证令牌的方式 jwtSpec.jwkSetUri(jwkEndpoint); }) .and().build(); } }
以同样的方式,我们可以配置公钥,而不是指定公开公钥的 URI。唯一的更改是调用 jwtSpec 实例的 publicKey() 方法,并提供一个有效的公钥作为参数。您可以使用我们在《OAuth 2:实现资源服务器》和《OAuth 2 : 使用 JWT 和加密签名》中讨论的任何方法,其中我们详细分析了资源服务器验证访问令牌的方法。
接下来,我们更改 application.properties文件以添加密钥集被曝露的 URI 的值,以及将服务器端口更改为9090。 这样,我们允许 KeyCloak 在 8080上运行。在下一个代码片段中, 您可以找到 application.properties 文件的内容:
server.port=9090 jwk.endpoint=http://localhost:8080/auth/realms/master/protocol/openid-connect/certs
让我们运行并证明应用程序具有我们想要的预期行为。我们使用本地安装的 Keycloak 服务器生成访问令牌:
curl -XPOST 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=password' \ --data-urlencode 'username=bill' \ --data-urlencode 'password=12345' \ --data-urlencode 'client_id=fitnessapp' \ --data-urlencode 'scope=fitnessapp'
在 HTTP 响应体中,我们接收到如下所示的访问令牌:
{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...", "expires_in": 6000, "refresh_expires_in": 1800, "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5c... ", "token_type": "bearer", "not-before-policy": 0, "session_state": "610f49d7-78d2-4532-8b13-285f64642caa", "scope": "fitnessapp" }
使用访问令牌,我们像这样调用应用程序的 /hello 端点:
curl -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMSE9zT0VRSmJuTmJVbjhQb VpYQTlUVW9QNTZoWU90YzNWT2swa1V2ajVVIn...' \ 'http://localhost:9090/hello'
响应体是
Hello!
总结
响应式应用程序在处理数据和与其他组件交换消息方面具有不同的风格。在某些情况下,响应式应用程序可能是更好的选择,比如我们可以将数据分割成单独的小段进行处理和交换。
与任何其他应用程序一样,您还需要使用安全配置来保护响应式应用程序。Spring Security 提供了一组优秀的工具,您可以使用它们为响应式应用程序和非响应式应用程序应用安全配置。
为了用 Spring Security 在响应式应用中实现用户管理,我们使用了 ReactiveUserDetailsService 契约。该组件的作用与 UserDetailsService 对于非响应式应用的作用相同:它告诉应用如何获取用户详细信息。
要实现响应式 web 应用程序的端点授权规则,您需要创建一个 SecurityWebFilterChain 类型的实例,并将其添加到 Spring 上下文。您可以使用 ServerHttpSecurity 构建器创建 SecurityWebFilterChain 实例
通常,用于定义授权配置的方法的名称与用于非响应式应用程序的方法的名称相同。但是,您会发现与响应式术语相关的一些小的命名差异。例如,不使用 authorizeRequests(),响应式应用程序对应的名称是 authorizeExchange()。
Spring Security 还提供了一种方法来定义方法级的授权规则,称为响应式方法安全,它提供了在响应式应用程序的任何层应用授权规则的极大灵活性。这类似于我们对非响应式应用程序所称的全局方法安全。
然而,对于非响应式应用程序,响应式方法安全并不像全局方法安全那样成熟。您已经可以使用 @PreAuthorize 和 @PostAuthorize 注解,但是 @PreFilter 和 @PostFilter 的功能还有待开发。