Spring Boot 4.1.2(2026-10-23)修了资源服务器 JWT 自动配置的一处空串处理:只配了 jwk-set-uri、却把 issuer-uri 写成空字符串时,4.1.1 仍会挂上 JwtIssuerValidator(""),带正常 iss 的令牌直接 401;4.1.2 改成有文本才加校验,同令牌放行。

许可证 Apache-2.0。仓库 spring-projects/spring-boot。官博见 Spring Boot 4.1.2 available now,条目对应 #52260。

问题怎么来的

常见写法是环境变量可有可无:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://127.0.0.1:9090/jwks
spring.security.oauth2.resourceserver.jwt.issuer-uri=${ISSUER_URI:}

ISSUER_URI 没设时,属性值是空串,不是 null。4.1.1 的 getValidator() 用 != null 判断,空串也会装上 JwtIssuerValidator,于是只接受 iss 恰好等于空串的令牌。正常 IdP 签发、带真实 issuer 的 JWT,解码阶段就会报 The iss claim is not valid。

4.1.2 改成 StringUtils.hasText(...)。提交 e28f3b61027d(See gh-52249),再经 43dfe6bb61 合进 4.1.x,关 #52260。

// 4.1.1
if (this.properties.getIssuerUri() != null) {
  validators.add(new JwtIssuerValidator(this.properties.getIssuerUri()));
}

// 4.1.2
if (StringUtils.hasText(this.properties.getIssuerUri())) {
  validators.add(new JwtIssuerValidator(this.properties.getIssuerUri()));
}

对照怎么搭

本地起一个 JWKS(RSA、kid=demo-key-1),再各起独立进程:Boot 4.1.1 听 8411,Boot 4.1.2 听 8412。两边配置相同,环境变量 ISSUER_URI 都留空。受保护接口:

@GetMapping("/api/me")
String me(JwtAuthenticationToken auth) {
  Jwt jwt = auth.getToken();
  Object iss = jwt.getClaims().get("iss");
  return "sub=" + jwt.getSubject() + "; iss=" + iss + "; boot-check=ok";
}

用同一把私钥签 RS256 令牌,sub=alice,iss=https://auth.example.com。

实测

两个进程已分别启动(摘录):

:: Spring Boot ::                (v4.1.1)
Tomcat started on port 8411 (http) with context path '/'

:: Spring Boot ::                (v4.1.2)
Tomcat started on port 8412 (http) with context path '/'

同一令牌打过去。命令与日志一致:

curl -sS -D - -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8411/api/me
curl -sS -D - -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8412/api/me

4.1.1 回 401(摘录):

HTTP/1.1 401
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The iss claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://127.0.0.1:8411/.well-known/oauth-protected-resource"

4.1.2 回 200(摘录):

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8

sub=alice; iss=https://auth.example.com; boot-check=ok

再签一枚 iss 为空串的令牌打 4.1.1,得到 200 且 body 为 sub=alice; iss=; boot-check=ok。说明旧逻辑并不是“没校验 issuer”,而是在拿空串当期望值。

怎么用

升到 Spring Boot 4.1.2(或已合入同修复的 4.0.9 线)。若仍停留在旧版,不要把可选的 issuer-uri 写成 ${ENV:} 这种会落成空串的占位;需要校验 issuer 时填真实地址,不需要就干脆别声明该属性。只配 jwk-set-uri 时,4.1.2 起不会再因为空的 issuer-uri 把带正常 iss 的令牌校验失败。

— 感谢阅读 —
广告

一起交流

分享你的思考,让讨论更进一步。