黑马商城JWT签名验证问题(RSA加密算法)

问题描述

黑马商城是一个微服务项目,其中user-service模块包含用户登录,验证密码后生成JWT并返回给客户端。hm-gateway网关模块则用于统一鉴权,需要验证JWT并解析出其中的用户信息。

该项目JWT使用的是rs256签名算法,即非对称加密算法。也就是说只有用户模块需要用私钥去签名生成JWT,项目中也给出了使用keytool生成密钥对文件hmall.jks的命令,如下:

keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123

命令中的参数与配置文件中的一一对应,有密钥别名、加密算法、密钥密码、密钥输出路径、密钥库密码,生成的hmall.jks放在resources文件夹下。该文件包含私钥以及证书(证书中包含公钥)

只有在用户登录时,才需要使用RSA私钥进行签名,在网关中只用公钥就可以验证签名和解析JWT,因此将hmall.jks文件的副本放在网关服务中是不安全的,增加了私钥泄露的可能,也违背了非对称加密算法的初衷(个人观点,如有不当请指出)。因此,我们需要将hmall.jks中的证书导出来,交给网关服务。(目前大部分工具都支持直接用证书验证签名)

代码修改流程

1. 首先,从密钥对文件hmall.jks中导出证书(或公钥),使用如下命令(需要注意在hmall.jks所在目录下执行)

keytool -exportcert -alias hmall -keystore hmall.jks -file hmall.cer -storepass hmall123 -rfc   
# 导出文件名为hmall.cer,rfc参数指定以PEM格式输出(不加则默认DER格式)

2. 然后,把导出的hmall.cer证书文件放到hm-gateway服务下的resources目录下,并把原来的密钥对文件删除。

3. 修改 application.yaml 配置文件,将密钥对文件位置换为证书文件位置。

server:
  port: 8080
hm:
  jwt:
    location: classpath:hmall.cer
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123

 4. 修改 SecurityConfig.java 配置文件,将获取密钥对的代码删除,换成获取公钥的代码,如下:

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 获取公钥
     * @param jwtProperties
     * @return
     * @throws CertificateException
     * @throws IOException
     */
    @Bean
    public PublicKey publicKey(JwtProperties jwtProperties) throws CertificateException, IOException {
        Certificate certificate = CertificateFactory
                .getInstance("X.509")
                .generateCertificate(jwtProperties.getLocation().getInputStream());
        return certificate.getPublicKey();
    }
}

 5. 修改 JwtTool.java 文件,创建签名器的时候使用公钥而不是密钥对。

@Component
public class JwtTool {
    private final JWTSigner jwtSigner;

    public JwtTool(PublicKey publicKey) {
        this.jwtSigner = JWTSignerUtil.createSigner("rs256", publicKey);
    }

    /**
     * 解析token
     *
     * @param token token
     * @return 解析刷新token得到的用户信息
     */
    public Long parseToken(String token) {
        // 1.校验token是否为空
        if (token == null) {
            throw new UnauthorizedException("未登录");
        }
        // 2.校验并解析jwt
        JWT jwt;
        try {
            jwt = JWT.of(token).setSigner(jwtSigner);
        } catch (Exception e) {
            throw new UnauthorizedException("无效的token", e);
        }
        // 2.校验jwt是否有效
        if (!jwt.verify()) {
            // 验证失败
            throw new UnauthorizedException("无效的token");
        }
        // 3.校验是否过期
        try {
            JWTValidator.of(jwt).validateDate();
        } catch (ValidateException e) {
            throw new UnauthorizedException("token已经过期");
        }
        // 4.数据格式校验
        Object userPayload = jwt.getPayload("user");
        if (userPayload == null) {
            // 数据为空
            throw new UnauthorizedException("无效的token");
        }

        // 5.数据解析
        try {
           return Long.valueOf(userPayload.toString());
        } catch (RuntimeException e) {
            // 数据格式有误
            throw new UnauthorizedException("无效的token");
        }
    }
}

小结

这样进一步加强了私钥的安全,即使公钥泄露,只是可以验证签名和解析JWT,但如果没有私钥,是无法篡改JWT的。

### JWT 中的 RSA 签名算法详解 #### 什么是 JWT 和 JWS? JSON Web Token (JWT) 是一种开放标准 (RFC 7519),用于在网络应用环境间安全地传输信息。当提到使用 RSA 进行签名时,实际上是指 JSON Web Signature (JWS)[^1]。 #### 使用 RSAJWT 进行签名的过程 为了确保消息的真实性和完整性,在发送方创建 JWT 后,会利用私钥对其进行签名。接收方则通过对应的公钥验证签名的真实性。具体过程如下: - **Header**: 包含令牌类型 (`typ`) 及所采用的加密算法 (`alg`), 如 `RS256` 表明采用了 SHA-256 哈希函数配合 RSA 加密方法[^2]: ```json { "typ": "JWT", "alg": "RS256" } ``` - **Payload**: 载荷部分包含了声明数据,这些信息会被编码成 Base64Url 字符串形式附加到 Header 后面形成待签发的内容。 - **Signature**: 发送者使用自己的私钥对前面两部分内容(即 header 和 payload 的连接字符串)加上特定前缀后计算哈希摘要并进行加密得到最终的签名值。这个操作保证了只有持有相同私钥的人才能生成有效的签名;而任何拥有相应公钥的人都能解码和校验该签名的有效性[^3]. ```python import jwt from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) token = jwt.encode({"some": "payload"}, private_key, algorithm='RS256') print(token.decode('utf-8')) ``` 上述 Python 示例展示了如何使用 PyJWT 库来创建带有 RS256 算法签名JWT 。这里需要注意的是,实际应用场景下应当妥善保管好私钥文件,并遵循最佳实践指南以保障系统的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值