Spring GateWay OAuth2ResourceServer 配置ServerHttpSecurity中的hasRole 无效的问题

当Spring Gateway作为OAuth2ResourceServer时,`hasRole`配置无效,原因是默认的`JwtGrantedAuthoritiesConverter`仅读取'scope'和'scp'作为权限。解决方法是自定义`ReactiveAuthenticationManager`,并覆盖转换器以包含jwt中的'authorities'字段。在`ServerHttpSecurity`中添加自定义配置,确保`hasRole`中的值与jwt负载中的权限值匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


文章原地址

问题

当gateway充当OAuth2ResourceServer的时候,会出现hasRole配置无效的问题。

原因来自于ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec中默认的ReactiveAuthenticationManager没有将jwt中authorities 的负载部分当做Authentication的权限。

简而言之,我们需要把jwt 的Claim中authorities的值加入 这里有个人提出了这个问题,但是官方认为仅仅使用scope``scp这两个字段当做权限就足够了github issue

解决方案

我们重新定义一个ReactiveAuthenticationManager权限管理器 默认的权限管理器使用jwtGrantedAuthoritiesConverter作为默认的转换器,就是这个转换器默认只读取"scope", "scp"这两个作为权限。

JwtGrantedAuthoritiesConverter源码

来看源码,源码不长。
convert方法可以理解为转换器的调用入口,从这个方法开始看

/**
 * Extracts the {@link GrantedAuthority}s from scope attributes typically found in a
 * {@link Jwt}.
 *
 * @author Eric Deandrea
 * @since 5.2
 */
public final class JwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
	private static final String DEFAULT_AUTHORITY_PREFIX = "SCOPE_";

	private static final Collection<String> WELL_KNOWN_AUTHORITIES_CLAIM_NAMES =
			Arrays.asList("scope", "scp");

	private String authorityPrefix = DEFAULT_AUTHORITY_PREFIX;

	private String authoritiesClaimName;

	/**
	 * Extract {@link GrantedAuthority}s from the given {@link Jwt}.
	 *
	 * @param jwt The {@link Jwt} token
	 * @return The {@link GrantedAuthority authorities} read from the token scopes
	 */
	@Override
	public Collection<Grant
<think>好的,用户之前问过关于JwtDecoder设置时区的问题,现在又问了如何在Spring Security OAuth2 Resource Server中使用publicKey字符串。我需要看看这两个问题之间有没有关联,或者用户是否在构建自己的资源服务器时遇到了配置问题。 用户这次的需求很明确,就是不想使用文件或URL来加载公钥,而是直接使用字符串形式的公钥。这可能是因为他们希望将公钥嵌入到配置文件中,或者从环境变量中读取,避免维护额外的文件。这在实际部署中可能更方便,尤其是云原生应用,配置通常通过环境变量或配置中心管理。 首先,我需要回忆Spring Security OAuth2 Resource Server的配置方式。通常,配置公钥是通过jwk-set-uri或者直接指定公钥的位置,比如文件。但用户希望用字符串,所以可能需要直接解析公钥字符串成PublicKey对象。 接下来,我需要确定如何将PEM格式的公钥字符串转换为Java的PublicKey对象。用户提供的公钥可能以-----BEGIN PUBLIC KEY-----开头,这是标准的PEM格式。Java需要的是RSAPublicKey实例,所以需要解析PEM字符串,提取Base64编码的内容,转换为X.509证书,然后获取公钥。 然后,需要考虑如何在Spring配置中注入这个PublicKey。通常,JwtDecoder是通过NimbusJwtDecoder的withPublicKey方法创建的,所以需要将字符串转换为PublicKey后传入。 可能的步骤是:编写一个工具类,将PEM字符串转换为PublicKey;然后在配置类中创建Bean,比如PublicKey publicKey(),调用这个工具方法;最后创建JwtDecoder时使用这个PublicKey。 需要注意异常处理,比如无效的PEM格式,或者解析过程中的错误,应该抛出明确的异常信息,方便用户调试。 另外,用户可能有疑问,如何验证配置是否正确。可能需要建议用户使用生成的JwtDecoder来解析一个测试Token,或者检查日志是否有解码错误。 还需要考虑是否支持不同的算法,比如RSA或ECDSA。用户提供的公钥类型可能会影响解析方式,但常见的是RSA,所以示例可能以RSA为例。 最后,确保代码示例正确,比如正确处理PEM头尾,移除换行符和空格,正确解码Base64内容,然后使用KeyFactory生成公钥对象。 可能还需要提到依赖,比如Bouncy Castle库,但Java标准库应该可以处理X.509证书的解析,所以可能不需要额外依赖。 总结步骤:解析PEM字符串为PublicKey,配置JwtDecoder使用这个PublicKey,并验证配置是否正确。</think>--- ### 一、核心解决方案 **在 Spring Security OAuth2 Resource Server 中,可以通过以下步骤直接使用 PEM 格式的公钥字符串配置 JWT 验证:** --- ### 二、具体实现步骤 #### 1. 准备公钥字符串 确保公钥为标准 PEM 格式: ```java String publicKeyPem = """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo ...(省略实际内容)... ZKLk0KgWwIDAQAB -----END PUBLIC KEY-----"""; ``` --- #### 2. 创建公钥转换工具类 ```java import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class KeyUtils { public static PublicKey parsePublicKey(String pem) { try { // 去除 PEM 头尾标记和换行符 String publicKeyContent = pem .replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); // 移除所有空白字符 // Base64 解码 byte[] keyBytes = Base64.getDecoder().decode(publicKeyContent); // 创建 X.509 格式密钥规范 X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); // 使用 RSA 算法生成公钥 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException("公钥解析失败", e); } } } ``` --- #### 3. 配置 JwtDecoder 在 Spring Security 配置类中注入 PublicKey: ```java import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @Configuration public class SecurityConfig { @Bean public PublicKey publicKey() { String publicKeyPem = "-----BEGIN PUBLIC KEY-----..."; // 替换为实际公钥 return KeyUtils.parsePublicKey(publicKeyPem); } @Bean public JwtDecoder jwtDecoder(PublicKey publicKey) { return NimbusJwtDecoder.withPublicKey(publicKey).build(); } } ``` --- ### 三、关键验证点 | 验证项 | 操作指引 | |----------------------|------------------------------------------------------------------------| | **公钥格式验证** | 检查是否包含 `BEGIN/END PUBLIC KEY` 标记,内容为 Base64 编码的 DER 格式 | | **算法匹配验证** | 确认 JWT 签名算法(如 RS256)与公钥类型(RSA)一致 | | **解码测试** | 使用有效/无效令牌测试 `JwtDecoder` 的解析能力 | --- ### 四、常见问题排查 #### 1. `InvalidKeySpecException` 错误 **原因**:公钥格式错误或算法不匹配 **解决方案**: - 使用 OpenSSL 验证公钥有效性: ```bash openssl rsa -pubin -in public_key.pem -text ``` - 确保公钥生成命令正确: ```bash openssl genpkey -algorithm RSA -out private_key.pem openssl rsa -pubout -in private_key.pem -out public_key.pem ``` --- #### 2. 令牌验证失败 **调试方法**: ```java @Bean public JwtDecoder jwtDecoder(PublicKey publicKey) { NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(publicKey).build(); decoder.setJwtValidator(new JwtValidator() { @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { System.out.println("JWT 头信息:" + jwt.getHeaders()); System.out.println("JWT 声明:" + jwt.getClaims()); return OAuth2TokenValidatorResult.success(); } }); return decoder; } ``` --- ### 五、扩展方案对比 | 配置方式 | 优点 | 缺点 | |------------------|---------------------------|--------------------------| | **公钥字符串** | 配置灵活,无需文件 | 需处理 PEM 格式转换 | | **jwk-set-uri** | 支持动态密钥轮换 | 依赖网络可用性 | | **证书文件** | 符合传统密钥管理习惯 | 需要维护物理文件 | --- ### 六、完整代码示例 [GitHub Gist 参考示例](https://gist.github.com/example/oauth2-public-key-config) (包含完整的 PEM 处理工具类和异常处理)
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值