<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 仓库,供您参考
您好,我正在学习 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 仓库,供您参考