使用 Spring OAuth 进行 JWT 不记名授权的 OAuth 2 客户端

Siv*_*r K 6 java rest spring oauth-2.0 spring-boot

在 org.springframework.security.oauth2.client.token.grant 包下的 Spring OAUTH 库中,我们为客户端、代码、隐式和密码提供了授权。

有一些扩展授权,例如 jwt-bearer 或 SAML,需要发送断言以生成令牌。

授予类型:urn:ietf:params:oauth:grant-type:jwt-bearer

Spring OAUTH 2 是否支持这些扩展授权。如果是的话使用哪个类?我们需要编写一个客户类来支持它吗?

Pie*_*e C 0

作为起点,您可以使用以下代码手动处理 OAuth2 JWT 承载流,该代码基本上创建一个过滤器来配置WebClient将连接到资源服务器的过滤器。

过滤器创建签名的 JWT,您将发送到授权服务器以获取将在最终请求标头中用作承载令牌的访问令牌。

请注意,您引用的流程是在RFC-7523(第 2.1 条)规范中定义的。

这是过滤器:


private ExchangeFilterFunction jwtAuthorizationGrantFilter() {
    return (request, next) -> {

        // Create a JWT with the required claims
        String jwtToken = JWT.create()
                .withClaim("iss", "3eeff86e-xxxxx-beafaa5ee66f")
                .withClaim("sub", "a1e757a3-xxxxx-77c5780b9284")
                .withClaim("aud", "account-d.docusign.com")
                .withIssuedAt(Instant.now())
                .withExpiresAt(Instant.now().plus(100, ChronoUnit.MINUTES))
                .withClaim("scope", "signature impersonation")
                .sign(algorithm);


        // Send the JWT to the authorization server and get an access token
        return webClient.post()
                .uri("https://account-d.docusign.com/oauth/token") // Your authorization server token endpoint here
                .accept(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromFormData("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
                        .with("assertion", jwtToken))
                .retrieve()
                .onStatus(HttpStatus::isError, response -> {
                    logTraceResponse(log, response);
                    return response.createException().flatMap(Mono::error);
                })
                .bodyToMono(OAuth2AccessTokenResponse.class)
                // Add the access token as a header to the original request
                .map(tokenResponse -> tokenResponse.getAccessToken().getTokenValue())
                .flatMap(token -> next.exchange(ClientRequest.from(request)
                            .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
                            .build()));
    };
}
Run Code Online (Sandbox Code Playgroud)

唯一需要的依赖项如下,其中 auth0 用于创建上面的 JWT:

implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.auth0:java-jwt:4.4.0")
Run Code Online (Sandbox Code Playgroud)

然后将过滤器应用到WebClient

implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.auth0:java-jwt:4.4.0")
Run Code Online (Sandbox Code Playgroud)

然后,唯一缺少的部分是处理公钥和私钥来签署 JWT :

private WebClient dsWebClient = WebClient.builder()
        .filter(jwtAuthorizationGrantFilter())
        .build();

public Mono<String> makeApiCall() {
    return dsWebClient.get()
            .uri("https://account-d.docusign.com/oauth/userinfo")
            .retrieve()
            .bodyToMono(String.class);
}
Run Code Online (Sandbox Code Playgroud)

该解决方案可能会在每次请求时请求访问令牌。

一个更复杂的解决方案将使用 Spring 的ReactiveClientRegistrationRepositoryServerOAuth2AuthorizedClientRepository自动配置客户端注册和授权客户端,但缺乏使用此 JWT Flow 的 Spring Boot CLIENT 示例/文档使得创建此增强的解决方案成为一个真正的难题。