容器化Spring Boot资源服务器后无法验证OAuth2 Access Token问题
问题描述
我用Spring Boot结合OAuth2保护REST API,依赖配置如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> </dependency>
application.properties配置:
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://authServer.com/auth/realms/api-dev
授权服务器是容器化部署的Keycloak。非容器化启动资源服务器时,能正常验证客户端Access Token并访问受保护资源;但容器化后,Postman请求返回401错误,容器日志显示连接超时:
org.springframework.security.authentication.AuthenticationServiceException: An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://authServer.com/auth/realms/api-dev/protocol/openid-connect/certs": Connection timed out (Connection timed out); nested exception is java.net.ConnectException: Connection timed out (Connection timed out) Caused by: org.springframework.security.oauth2.jwt.JwtException: An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://authServer.com/auth/realms/api-dev/protocol/openid-connect/certs": Connection timed out (Connection timed out); nested exception is java.net.ConnectException: Connection timed out (Connection timed out) at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:169) ~[spring-security-oauth2-jose-5.7.4.jar!/:5.7.4] at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.decode(NimbusJwtDecoder.java:137) ~[spring-security-oauth2-jose-5.7.4.jar!/:5.7.4] at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:97) ~[spring-security-oauth2-resource-server-5.7.4.jar!/:5.7.4] ... 55 common frames omitted Caused by: com.nimbusds.jose.RemoteKeySourceException: Couldn't retrieve remote JWK set: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://authServer.com/auth/realms/api-dev/protocol/openid-connect/certs": Connection timed out (Connection timed out); nested exception is java.net.ConnectException: Connection timed out (Connection timed out)
需要解决容器化后无法验证Access Token的问题。
解决方案
1. 排查容器网络连通性
- 进入资源服务器容器内部,执行
curl https://authServer.com/auth/realms/api-dev/protocol/openid-connect/certs,确认是否能正常返回JWK集合。如果curl也超时,说明容器到Keycloak的网络存在问题:- 若Keycloak和资源服务器在同一Docker网络,改用容器名称或服务名访问,比如
http://keycloak-container-name/auth/realms/api-dev(注意如果Keycloak启用HTTPS,需确保容器内信任其证书)。 - 若Keycloak部署在外部服务器,检查容器所在主机的防火墙、安全组是否允许容器访问Keycloak的443端口;同时确认容器的DNS配置正确,能正常解析
authServer.com域名。
- 若Keycloak和资源服务器在同一Docker网络,改用容器名称或服务名访问,比如
2. 配置JWK请求超时与缓存
Spring Security默认的JWK请求未设置合理超时,容器网络环境下容易触发超时。可以自定义JwtDecoder,添加超时配置并启用JWK缓存:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplateBuilder; import java.time.Duration; @Configuration public class JwtConfig { @Bean public JwtDecoder jwtDecoder() { RestTemplate restTemplate = new RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(10)) .setReadTimeout(Duration.ofSeconds(10)) .build(); NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation("https://authServer.com/auth/realms/api-dev") .restOperations(restTemplate) .build(); // 配置JWK缓存,避免频繁请求Keycloak jwtDecoder.setJwkSetCache(new org.springframework.security.oauth2.jwt.DefaultJwkSetCache(Duration.ofHours(1))); return jwtDecoder; } }
3. 处理Keycloak证书信任问题
如果Keycloak使用自签名证书,容器内的JVM默认不会信任该证书,导致连接失败:
- 将Keycloak的证书导出,添加到资源服务器容器的JVM信任库中,可在Dockerfile中添加以下步骤:
COPY keycloak.crt /tmp/ RUN keytool -importcert -file /tmp/keycloak.crt -alias keycloak -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -noprompt
- 或者在启动容器时指定自定义信任库参数:
docker run -e JAVA_OPTS="-Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit" your-resource-server-image
4. 验证Keycloak Issuer配置一致性
检查Keycloak Realm的Issuer设置,确保其对外暴露的地址和资源服务器配置的issuer-uri完全一致:
- 登录Keycloak管理后台,进入目标Realm的
Realm Settings→General→Frontend URL,设置为正确的外部访问地址(如https://authServer.com/auth/realms/api-dev),避免JWK响应中的Issuer值与资源服务器配置不匹配。
内容的提问来源于stack exchange,提问作者Severin Mbekou
相关产品推荐
相关产品推荐

