创建GlobalFilter验证RSA令牌时遇类型转换错误求解决
问题:Spring Gateway中RSA公钥类型转换失败
错误信息
No converter found capable of converting from type [java.lang.String] to type [java.security.interfaces.RSAPublicKey]
相关代码
LoggingGlobalPreFilter.java
@Component public class LoggingGlobalPreFilter implements GlobalFilter { final Logger logger = LoggerFactory.getLogger(LoggingGlobalPreFilter.class); private RsaKeyPropreties Rsakeys; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { logger.info("Pre-Filter executed"); String requestPath = exchange.getRequest().getPath().toString(); logger.info("Request path = " + requestPath); HttpHeaders headers = exchange.getRequest().getHeaders(); Set<String> headerNames = headers.keySet(); headerNames.forEach((header) -> { if(header.equals("Authorization")){ SignedJWT sign; try { String token = headers.get(header).toString(); if (token == null) { token=""; } sign = SignedJWT.parse(token); JWSVerifier verifier = new RSASSAVerifier(Rsakeys.publickey()); logger.info(header + " " + sign.verify(verifier)); } catch (ParseException | JOSEException e) { e.printStackTrace(); } } }); return chain.filter(exchange); } }
RoutingApplication.java
@EnableConfigurationProperties(RsaKeyPropreties.class) @EnableDiscoveryClient @SpringBootApplication public class RoutingApplication { public static void main(String[] args) { SpringApplication.run(RoutingApplication.class, args); } }
RsaKeyPropreties.java
@ConfigurationProperties(prefix = "rsa") public record RsaKeyPropreties(RSAPublicKey publickey,RSAPrivateKey privatekey) { }
application.properties
rsa.publickey=classpath:routing/public.pem rsa.privatekey=classpath:routing/private.pem
文件位置
RSA密钥文件位于项目src/main/resources/routing目录下,包含public.pem和private.pem两个文件。
修复方案
Spring默认没有提供从字符串(或classpath路径)到RSAPublicKey/RSAPrivateKey的转换器,需要自定义类型解析逻辑来处理PEM格式的密钥文件。
1. 创建RSA密钥解析工具类
import org.springframework.util.ResourceUtils; import java.security.KeyFactory; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RsaKeyParser { public static RSAPublicKey parsePublicKey(String resourcePath) throws Exception { // 读取PEM文件内容 String pemContent = new String(java.nio.file.Files.readAllBytes(ResourceUtils.getFile(resourcePath).toPath())); // 去除PEM头部、尾部及空白字符 String publicKeyPEM = pemContent.replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); // Base64解码并生成公钥 byte[] encoded = Base64.getDecoder().decode(publicKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } public static RSAPrivateKey parsePrivateKey(String resourcePath) throws Exception { String pemContent = new String(java.nio.file.Files.readAllBytes(ResourceUtils.getFile(resourcePath).toPath())); String privateKeyPEM = pemContent.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] encoded = Base64.getDecoder().decode(privateKeyPEM); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } }
注意:如果私钥是PKCS#1格式(头部为
-----BEGIN RSA PRIVATE KEY-----),需要先转换为PKCS#8格式,或者调整解析逻辑使用对应的密钥规范。
2. 注册自定义配置属性转换器
创建配置类,将上述解析逻辑绑定到Spring的配置属性转换流程中:
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @Configuration public class RsaKeyConverterConfig { @Bean @ConfigurationPropertiesBinding public Converter<String, RSAPublicKey> publicKeyConverter() { return source -> { try { return RsaKeyParser.parsePublicKey(source); } catch (Exception e) { throw new IllegalArgumentException("解析RSA公钥失败:" + source, e); } }; } @Bean @ConfigurationPropertiesBinding public Converter<String, RSAPrivateKey> privateKeyConverter() { return source -> { try { return RsaKeyParser.parsePrivateKey(source); } catch (Exception e) { throw new IllegalArgumentException("解析RSA私钥失败:" + source, e); } }; } }
3. 补充依赖(可选但推荐)
如果项目缺少密钥解析相关依赖,在pom.xml中添加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency>
4. 修复过滤器中的隐藏问题
- 原代码中
Rsakeys未注入,会导致空指针,添加构造函数注入:private final RsaKeyPropreties rsaKeys; public LoggingGlobalPreFilter(RsaKeyPropreties rsaKeys) { this.rsaKeys = rsaKeys; } - 修正Token提取逻辑(原代码会把Header值转为
[Bearer xxx]格式的字符串):String token = headers.getFirst(header); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); } else { token = ""; }
内容的提问来源于stack exchange,提问作者Rafael Souza
相关产品推荐
相关产品推荐

