【微服务JWT】使用RSA工具生成公钥和私钥(第一部分)

1.JWT概述:

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io

token需要加密,进行保护,采用 RSA 加密

2.RSA加密概述:

RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可逆的”密码体制。

在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK [2]。

RSA加密:非对称加密。

同时生产一对秘钥:公钥和私钥。

公钥秘钥:用于加密

私钥秘钥:用于解密

既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密私钥负责解密;同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名公钥负责验证

使用RSA加密保证token数据在传输过程中不会被篡改。

3.环境搭建(父项目pom文件JWT工具类)

<jwt.jjwt.version>0.9.0</jwt.jjwt.version>
<jwt.joda.version>2.9.7</jwt.joda.version>
<beanutils.version>1.9.3</beanutils.version>


<!--jwt-->
<!--JavaBean工具类,用于JavaBean数据封装-->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>${beanutils.version}</version>
</dependency>
<!--jwt工具-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jwt.jjwt.version}</version>
</dependency>
<!--joda 时间工具类 -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>${jwt.joda.version}</version>
</dependency>

4.环境搭建(网关pom文件所需工具类)

        <!--jwt-->
        <!--JavaBean工具类,用于JavaBean数据封装-->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
        </dependency>
        <!--jwt工具-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <!--joda 时间工具类 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>

5.所需拷贝工具

JwtUtils:

package com.czxy.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.DateTime;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Created by liangtong.
 */
public class JwtUtils {
    /**
     *  私钥加密token
     * @param data 需要加密的数据(载荷内容)
     * @param expireMinutes 过期时间,单位:分钟
     * @param privateKey 私钥
     * @return
     */
    public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey)  {
        try {
            //1 获得jwt构建对象
            JwtBuilder jwtBuilder = Jwts.builder();
            //2 设置数据
            if( data == null ) {
                throw new RuntimeException("数据不能为空");
            }
            BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获得属性名
                String name = propertyDescriptor.getName();
                // 获得属性值
                Object value = propertyDescriptor.getReadMethod().invoke(data);
                if(value != null) {
                    jwtBuilder.claim(name,value);
                }
            }
            //3 设置过期时间
            jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
            //4 设置加密
            jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
            //5 构建
            return jwtBuilder.compact();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过公钥解析token
     * @param token 需要解析的数据
     * @param publicKey 公钥
     * @param beanClass 封装的JavaBean
     * @return
     * @throws Exception
     */
    public static <T> T  getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
        //1 获得解析后内容
        Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
        //2 将内容封装到对象JavaBean
        T bean = beanClass.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 通过属性名,获得对应解析的数据
            Object value = body.get(name);
            if(value != null) {
                // 将获得的数据封装到对应的JavaBean中
                BeanUtils.setProperty(bean,name,value);
            }
        }
        return bean;
    }
}

RsaUtils:

package com.czxy.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by liangtong.
 */
public class RsaUtils {

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws Exception
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);

        //创建父文件夹
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdirs();
        }
        //创建需要的文件
        if (!dest.exists()) {
            dest.createNewFile();
        }

        Files.write(dest.toPath(), bytes);
    }


}

6.测试

package com.czxy;

import com.czxy.utils.RsaUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Test;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 * @description
 */
public class TestRSA {

    private static final String pubKeyPath = "D:\\rsa\\rsa.pub";

    private static final String priKeyPath = "D:\\rsa\\rsa.pri";


    @Test
    public void testGenerate() throws Exception {
        RsaUtils.generateKey(pubKeyPath, priKeyPath, "1234");
    }

    @Test
    public void testGet() throws Exception {
        PublicKey publicKey = RsaUtils.getPublicKey(pubKeyPath);
        System.out.println(publicKey);

        PrivateKey privateKey = RsaUtils.getPrivateKey(priKeyPath);
        System.out.println(privateKey);
    }


}

需要注意点!!!

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JWT(JSON Web Token)的公钥私钥,主要涉及生成签名(签发令牌)和验证签名(验证令牌)两个过程。 1. 生成签名(签发令牌): - 使用私钥JWT 的头部和载荷进行数字签名,以确保令牌的完整性和真实性。 - 将签名后的结果添加到 JWT 的头部或载荷中,形成最终的 JWT。 2. 验证签名(验证令牌): - 获取 JWT 中的头部和载荷,并提取签名部分。 - 使用公钥对头部和载荷进行验证,以确认令牌是由合法的签发者签名的。 - 如果验证成功,则说明令牌是有效的。 在实际应用中,生成签名和验证签名的具体实现细节会根据编程语言和库的不同而有所差异。以下是一个示例,使用 C# 和 System.IdentityModel.Tokens.Jwt 库来生成和验证 JWT 的过程: ```csharp using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; public class JwtHelper { public static string GenerateToken(string privateKey) { var securityKey = new SymmetricSecurityKey(Convert.FromBase64String(privateKey)); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim("userId", "123") }), Expires = DateTime.UtcNow.AddDays(1), SigningCredentials = credentials }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } public static bool ValidateToken(string token, string publicKey) { var tokenHandler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(publicKey)), ValidateIssuer = false, // 可选,如果需要验证签发者,请将其设置为 true,并提供有效的 Issuer ValidateAudience = false // 可选,如果需要验证受众,请将其设置为 true,并提供有效的 Audience }; try { // 验证令牌 tokenHandler.ValidateToken(token, validationParameters, out _); return true; } catch (Exception) { // 令牌验证失败 return false; } } } ``` 上述代码中,`GenerateToken` 方法用于生成 JWT,其中传入私钥 `privateKey` 用于生成签名。在 `tokenDescriptor` 中,我们设置了 JWT 的主题(Subject)、过期时间(Expires)等信息,并使用私钥进行签名。 `ValidateToken` 方法用于验证 JWT,其中传入公钥 `publicKey` 用于验证签名。在 `validationParameters` 中,我们设置了验证签名的密钥(IssuerSigningKey)和其他可选的验证参数(如验证签发者和受众)。 请注意,上述示例中使用的是对称加密算法(HMAC),密钥是以 Base64 编码的字符串。如果使用非对称加密算法(如 RSA),则需要使用公钥私钥对,并且相应的密钥格式和库的使用可能会有所不同。 总之,通过使用 JWT公钥私钥,你可以生成签名并签发令牌,也可以验证令牌的签名的真实性和完整性。具体的实现方法会根据你所使用的编程语言和库而有所差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值