You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:45:42