<Spring Security> 当客户端调用资源服务器时,header 中不包含访问令牌

<Spring Security> Access token not included in header when client calls resource server

您好,我正在学习 Spring 安全性。我试图生成一个 OAuth2 客户端和资源服务器设置,基本上遵循 https://dzone.com/articles/implement-oauth-20-easily-with-spring-boot-and-spr 上的准则。

当尝试从客户端调用资源服务器上的端点时,它给出 HTTP 401。

2021-03-04 21:38:33.355 ERROR 99501 --- [ctor-http-nio-2] reactor.core.publisher.Operators         : Operator called default onErrorDropped

reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized: 401 Unauthorized from GET http://localhost:8081/api/resource/hello
Caused by: org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized: 401 Unauthorized from GET http://localhost:8081/api/resource/hello
    at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:181) ~[spring-webflux-5.3.4.jar:5.3.4]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ 401 from GET http://localhost:8081/api/resource/hello [DefaultWebClient]
Stack trace:
        at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:181) ~[spring-webflux-5.3.4.jar:5.3.4]
        at 

资源服务器上的端点受 OAuth2 保护。假设在提供凭据(在本例中为 okta)后,在后台通道中,客户端应用程序将获取授权代码,然后将其交换为访问令牌,然后在调用端点时在 header 中包含访问令牌资源服务器(如有误请指正)

以上都是后台做的,不知道客户端是怎么调用资源服务器的

我试图删除 spring 安全依赖项以在资源服务器上禁用 OAuth2。在这种情况下,对资源服务器的调用没有问题。

我已将源码放在 GitHub。

资源服务器的关键文件如下

OAuth2 资源服务器 HTTP 安全配置

package com.somecompany.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
            .and()
                .authorizeRequests()
                    .antMatchers("/", "/error", "/webjars/**", "/actuator/**")
                        .permitAll()
                    .anyRequest()
                        .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
    }
}

OAuth2 资源服务器控制器

package com.somecompany.controller;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/resource")
public class Oauth2DemoResourceServerController {

    @GetMapping("/hello")
    @CrossOrigin
    public String api() {
        return "Made it to protected api on resource server!";
    }
}

OAuth2 资源服务器属性

management.endpoints.web.exposure.include: httptrace

spring:
security:
    oauth2:
    resourceserver:
        jwt:
        jwk-set-uri: https://dev-27548664.okta.com/oauth2/default/v1/keys

OAuth2 资源服务器pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.somecompany</groupId>
    <artifactId>oauth2-demo-resource-server</artifactId>
    <version>1.0.0</version>
    <name>oauth2-demo-resource-server</name>
    <description>oauth2-demo-resource-server</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

[2021 年 3 月 31 日更新]

我在资源服务器上添加了一个过滤器,它显示对资源服务器的请求不包括身份验证 header,因此应该是 401。因此我怀疑这是 WebClient 配置的问题。有什么想法请看

我通过使用 servlet 方法更改 WebClient 配置,以某种方式使设置正常工作。
综上所述问题出在客户端,而不是资源服务器。

(1) 在客户端的 WebClient 配置中,使用 ClientRegistrationRepository 而不是 ReactiveClientRegistrationRepository(即按照 https://dzone.com/articles/implement-oauth-20-easily-with-spring-boot-and-spr 中描述的教程)。

(2) 添加“spring-boot-starter-web”依赖。这一步很重要,因为没有它,就找不到 ClientRegistrationRepository bean。

(3)更新pom.xml以刷新依赖关系。

(4)修改RestController中Webclient的使用,使用block()同步等待资源服务器响应


虽然设置工作正常,但我不确定如何使用 ReactiveClientRegistrationRepository 配置设置以使用异步方法。如果有人有任何想法,请随时分享,谢谢。

P.S。 所有更改已更新到上面提到的 Git 仓库,供您参考