Spring OAuth2授权服务器OpenID配置请求返回401未授权问题排查
问题描述
已部署一个授权服务器,另有依赖spring-security-oauth2-client组件的服务用于从该授权服务器获取令牌。授权服务器仅通过YAML配置客户端信息,无自定义配置类。测试环境中,服务启动时请求http://oauthz:8080/.well-known/openid-configuration和http://oauthz:8080/.well-known/oauth-authorization-server获取OpenID配置时,持续返回401 UNAUTHORIZED响应;但本地使用http://localhost:8080访问授权服务器则无此问题,需调整授权服务器配置解决该错误。
授权服务器YAML配置
logging: level: org.springframework.security: trace spring: security: oauth2: authorizationserver: client: oidc-client: registration: client-id: "rdocelec" client-secret: "{noop}xxx" client-authentication-methods: - "client_secret_basic" authorization-grant-types: - "client_credentials" scopes: - "api" require-authorization-consent: true
请求日志
HTTP GET http://oauthz:8080/.well-known/openid-configuration Accept=[application/json, application/cbor, application/*+json] sun.net.www.MessageHeader@20395afe5 pairs: {GET /.well-known/openid-configuration HTTP/1.1: null}{Accept: application/json, application/cbor, application/*+json}{User-Agent: Java/11.0.21}{Host: oauthz:8080}{Connection: keep-alive} sun.net.www.MessageHeader@4b1339bf16 pairs: {null: HTTP/1.1 401}{Vary: Origin}{Vary: Access-Control-Request-Method}{Vary: Access-Control-Request-Headers}{WWW-Authenticate: Basic realm="Realm"}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 0}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{X-Frame-Options: DENY}{WWW-Authenticate: Basic realm="Realm"}{Content-Length: 0}{Date: Thu, 16 Nov 2023 10:34:41 GMT}{Keep-Alive: timeout=60}{Connection: keep-alive} Response 401 UNAUTHORIZED HTTP GET http://oauthz:8080/.well-known/openid-configuration Accept=[application/json, application/cbor, application/*+json] sun.net.www.MessageHeader@451f08ea5 pairs: {GET /.well-known/openid-configuration HTTP/1.1: null}{Accept: application/json, application/cbor, application/*+json}{User-Agent: Java/11.0.21}{Host: oauthz:8080}{Connection: keep-alive} sun.net.www.MessageHeader@dcf495c16 pairs: {null: HTTP/1.1 401}{Vary: Origin}{Vary: Access-Control-Request-Method}{Vary: Access-Control-Request-Headers}{WWW-Authenticate: Basic realm="Realm"}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 0}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{X-Frame-Options: DENY}{WWW-Authenticate: Basic realm="Realm"}{Content-Length: 0}{Date: Thu, 16 Nov 2023 10:34:41 GMT}{Keep-Alive: timeout=60}{Connection: keep-alive} Response 401 UNAUTHORIZED HTTP GET http://oauthz:8080/.well-known/oauth-authorization-server Accept=[application/json, application/cbor, application/*+json] sun.net.www.MessageHeader@60610a2b5 pairs: {GET /.well-known/oauth-authorization-server HTTP/1.1: null}{Accept: application/json, application/cbor, application/*+json}{User-Agent: Java/11.0.21}{Host: oauthz:8080}{Connection: keep-alive} sun.net.www.MessageHeader@420dee8216 pairs: {null: HTTP/1.1 401}{Vary: Origin}{Vary: Access-Control-Request-Method}{Vary: Access-Control-Request-Headers}{WWW-Authenticate: Basic realm="Realm"}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 0}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{X-Frame-Options: DENY}{WWW-Authenticate: Basic realm="Realm"}{Content-Length: 0}{Date: Thu, 16 Nov 2023 10:34:41 GMT}{Keep-Alive: timeout=60}{Connection: keep-alive} Response 401 UNAUTHORIZED
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>3.1.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>net.gencat.transversal.espaidoc</groupId> <artifactId>oautz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>oauthz</name> <description>Oauth Authorization server</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>oauthz</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
解决方案
原因分析
从日志的WWW-Authenticate: Basic realm="Realm"可以看出,请求被Spring Security的默认认证拦截器拦截,说明授权服务器的元数据端点(/.well-known/**)未被配置为允许匿名访问。虽然Spring Authorization Server的自动配置应该默认开放这些端点,但可能因环境差异或配置优先级问题导致规则未生效。
解决步骤
方法1:通过YAML配置开放端点
在授权服务器的application.yml中添加Spring Security的授权规则,明确允许匿名访问元数据端点:
spring: security: authorization: authorize-requests: authorize: "/.well-known/**": permitAll "/oauth2/jwks": permitAll "/oauth2/authorize": authenticated "/oauth2/token": authenticated "/oauth2/revoke": authenticated "/oauth2/introspect": authenticated "/**": denyAll
方法2:添加自定义Security配置类
创建一个Spring Security配置类,显式配置端点的访问权限:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize // 开放元数据和JWKS端点 .requestMatchers("/.well-known/**", "/oauth2/jwks").permitAll() // 其他授权服务器端点需认证 .requestMatchers("/oauth2/authorize", "/oauth2/token", "/oauth2/revoke", "/oauth2/introspect").authenticated() // 其余端点拒绝访问 .anyRequest().denyAll() ); return http.build(); } }
验证修改
重启授权服务器后,测试环境的客户端服务应能正常获取OpenID配置,不再返回401错误。
内容的提问来源于stack exchange,提问作者Jordi
相关产品推荐
相关产品推荐

