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);
}
}