前置文章:
Spring Security-整合Spring Boot(分布式)-篇章一:主要介绍了JWT、RSA以及Security的认证校验逻辑;
前言:本文主要介绍JWT工具类,以及RSA工具类的基础使用。是一些固定的用法,可以跟据需求了解即可。
零、本文纲要
- 一、 代码实现
- 二、 security_parent(聚合管理)
- 三、 security_common(共用模块)
- domain/entity
- utils
- 使用工具类生成公私钥
一、 代码实现
1. 模块构成
security_parent(聚合管理)
security_common(共用模块)
security_auth_server(认证模块)
security_source_product(资源模块)
二、 security_parent(聚合管理)
- ① pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stone</groupId>
<artifactId>springboot_security_jwt_rsa_parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!--聚合管理-->
<modules>
<module>security_common</module>
<module>security_auth_server</module>
<module>security_source_product</module>
</modules>
<!--统一依赖管理-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>
</project>
三、 security_common(共用模块)
1. domain/entity
JWT: Header.Payload.Signature
此处我们自定义Payload类,用于承载我们想要存储在Token中的信息;
Payload包含:id、用户信息、过期时间。
@Data
public class Payload<T> {
private String id;
private T userInfo;
private Date expiration;
}
2. utils
注意:此处的工具类主要是把具体类的固定用法做了封装,实际按需了解即可。
- ① JsonUtils
public class JsonUtils {
public static final ObjectMapper mapper = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
public static String toString(Object obj) {
if (obj == null) {
return null;
}
if (obj.getClass() == String.class) {
return (String) obj;
}
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("json序列化出错:" + obj, e);
return null;
}
}
public static <T> T toBean(String json, Class<T> tClass) {
try {
return mapper.readValue(json, tClass);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <E> List<E> toList(String json, Class<E> eClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
public static <T> T nativeRead(String json, TypeReference<T> type) {
try {
return mapper.readValue(json, type);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
}
- ② JwtUtils
public class JwtUtils {
private static final String JWT_PAYLOAD_USER_KEY = "user";
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位[分钟]
* @return JWT
*/
public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder() //DefaultJwtBuilder
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo)) //DefaultClaims#LinkedHashMap
.setId(createJTI()) //key:"jti",value:createJTI()
.setExpiration(DateTime.now().plusMinutes(expire).toDate()) //key:"exp",value:"minutes"
.signWith(privateKey, SignatureAlgorithm.RS256) //设置加密算法 和 key
.compact(); //进行加密,返回jwt
}
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位[秒]
* @return JWT
*/
public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥
* @return Jws<Claims>
*/
private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
return Jwts.parser() //DefaultJwtParser
.setSigningKey(publicKey) //公钥解密
.parseClaimsJws(token); //需要解密的token
}
private static String createJTI() {
return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
}
/**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
claims.setExpiration(body.getExpiration());
return claims;
}
/**
* 获取token中的载荷信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setExpiration(body.getExpiration());
return claims;
}
}
- ③ RsaUtils
public class RsaUtils {
private static final int DEFAULT_KEY_SIZE = 2048;
/**
* 从文件中读取公钥
*
* @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
*/
private static PublicKey getPublicKey(byte[] bytes) throws Exception {
bytes = Base64.getDecoder().decode(bytes);
//用给定的编码密钥创建一个新的X509EncodedKeySpec,按照X.509标准编码的密钥
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
//生成公钥
return factory.generatePublic(spec);
}
/**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
bytes = Base64.getDecoder().decode(bytes);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
}
/**
* 根据密文,生成rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
//指定使用RSA算法
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
//将指定的"盐"封装到SecureRandom对象
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
//初始化生成器
keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
//生成钥匙
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
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.exists()) {
dest.createNewFile();
}
Files.write(dest.toPath(), bytes);
}
}
3. 使用工具类生成公私钥
此处以win系统为例,实际业务需调整Linux目录
public class RsaUtilsTest extends TestCase {
private String privateFilePath = "D:\\tmp\\auth\\id_key_rsa.pri";
private String publicFilePath = "D:\\tmp\\auth\\id_key_rsa.pub";
public void testGetPublicKey() throws Exception {
System.out.println(RsaUtils.getPublicKey(publicFilePath));
}
public void testGetPrivateKey() throws Exception {
System.out.println(RsaUtils.getPrivateKey(privateFilePath));
}
public void testGenerateKey() throws Exception {
RsaUtils.generateKey(publicFilePath, privateFilePath, "salt", 2048);
}
}
四、结尾
以上即为Spring Security-整合Spring Boot(分布式)-篇章二的全部内容,感谢阅读。
网友评论