【问题标题】:<Spring Security> Access token not included in header when client calls resource server<Spring Security> 客户端调用资源服务器时,标头中不包含访问令牌
【发布时间】:2021-06-03 00:39:02
【问题描述】:

您好,我正在学习 Spring Security。我试图生成一个 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),在后台通道中,客户端应用程序将获取授权代码,然后将其交换为访问令牌,然后在调用资源服务器上的端点时将访问令牌包含在标头中(如果我误解了,请纠正)。

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

我试图删除 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 日更新]

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

【问题讨论】:

  • 我认为您错过了在客户端中设置很多东西docs.spring.io/spring-security/site/docs/current/reference/…
  • 在客户端,我能够获取经过身份验证的对象并显示配置文件信息,因此我相信令牌交换确实发生并完成了。问题可能与从客户端到资源服务器的 HTTP 调用有关。不知道为什么没有在请求中添加身份验证标头。

标签: java spring-boot spring-security oauth-2.0 spring-security-oauth2


【解决方案1】:

我通过使用 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 使用异步方法。如果有谁有什么想法,请随时分享,谢谢。

附言 所有更改都已更新到上面提到的 Git repo 以供您参考

【讨论】:

    猜你喜欢
    • 2020-04-16
    • 1970-01-01
    • 2020-12-29
    • 2021-12-26
    • 2018-12-26
    • 2012-02-22
    • 2020-05-18
    • 2020-05-11
    • 2017-02-22
    相关资源
    最近更新 更多