【发布时间】: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