This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

40
blade-starter-jwt/pom.xml Normal file
View File

@@ -0,0 +1,40 @@
<?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>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-starter-jwt</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<dependencies>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,236 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt;
import lombok.SneakyThrows;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import reactor.util.annotation.Nullable;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
/**
* JwtCrypto
*
* @author Chill
*/
public class JwtCrypto {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
public static final String BLADE_TOKEN_CRYPTO_KEY = "blade.token.crypto-key";
/**
* Base64加密
*
* @param content 文本内容
* @param aesTextKey 文本密钥
* @return {String}
*/
public static String encryptToString(String content, String aesTextKey) {
return encodeToString(encrypt(content, aesTextKey));
}
/**
* Base64加密
*
* @param content 内容
* @param aesTextKey 文本密钥
* @return {String}
*/
public static String encryptToString(byte[] content, String aesTextKey) {
return encodeToString(encrypt(content, aesTextKey));
}
/**
* 加密
*
* @param content 文本内容
* @param aesTextKey 文本密钥
* @return byte[]
*/
public static byte[] encrypt(String content, String aesTextKey) {
return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
}
/**
* 加密
*
* @param content 文本内容
* @param charset 编码
* @param aesTextKey 文本密钥
* @return byte[]
*/
public static byte[] encrypt(String content, Charset charset, String aesTextKey) {
return encrypt(content.getBytes(charset), aesTextKey);
}
/**
* 加密
*
* @param content 内容
* @param aesTextKey 文本密钥
* @return byte[]
*/
public static byte[] encrypt(byte[] content, String aesTextKey) {
return encrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
}
/**
* Base64解密
*
* @param content 文本内容
* @param aesTextKey 文本密钥
* @return {String}
*/
@Nullable
public static String decryptToString(@Nullable String content, @Nullable String aesTextKey) {
if (!StringUtils.hasText(content) || !StringUtils.hasText(aesTextKey)) {
return null;
}
byte[] hexBytes = decrypt(decode(content.getBytes(DEFAULT_CHARSET)), aesTextKey);
return new String(hexBytes, DEFAULT_CHARSET);
}
/**
* 解密
*
* @param content 内容
* @param aesTextKey 文本密钥
* @return byte[]
*/
public static byte[] decrypt(byte[] content, String aesTextKey) {
return decrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
}
/**
* 解密
*
* @param content 内容
* @param aesKey 密钥
* @return byte[]
*/
public static byte[] encrypt(byte[] content, byte[] aesKey) {
return aes(Pkcs7Encoder.encode(content), aesKey, Cipher.ENCRYPT_MODE);
}
/**
* 加密
*
* @param encrypted 内容
* @param aesKey 密钥
* @return byte[]
*/
public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
return Pkcs7Encoder.decode(aes(encrypted, aesKey, Cipher.DECRYPT_MODE));
}
/**
* ase加密
*
* @param encrypted 内容
* @param aesKey 密钥
* @param mode 模式
* @return byte[]
*/
@SneakyThrows
private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
cipher.init(mode, keySpec, iv);
return cipher.doFinal(encrypted);
}
/**
* Base64-encode the given byte array to a String.
*
* @param src the original byte array
* @return the encoded byte array as a UTF-8 String
*/
public static String encodeToString(byte[] src) {
if (src.length == 0) {
return "";
}
return Base64.getEncoder().encodeToString(src);
}
/**
* Base64-decode the given byte array.
*
* @param src the encoded byte array
* @return the original byte array
*/
public static byte[] decode(byte[] src) {
if (src.length == 0) {
return src;
}
return Base64.getDecoder().decode(src);
}
/**
* 提供基于PKCS7算法的加解密接口.
*/
private static class Pkcs7Encoder {
private static final int BLOCK_SIZE = 32;
private static byte[] encode(byte[] src) {
int count = src.length;
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
// 获得补位所用的字符
byte pad = (byte) (amountToPad & 0xFF);
byte[] pads = new byte[amountToPad];
Arrays.fill(pads, pad);
int length = count + amountToPad;
byte[] dest = new byte[length];
System.arraycopy(src, 0, dest, 0, count);
System.arraycopy(pads, 0, dest, count, amountToPad);
return dest;
}
private static byte[] decode(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > BLOCK_SIZE) {
pad = 0;
}
if (pad > 0) {
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
return decrypted;
}
}
}

View File

@@ -0,0 +1,417 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.Getter;
import org.springblade.core.jwt.enums.SingleLevel;
import org.springblade.core.jwt.props.JwtProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
/**
* Jwt工具类
*
* @author Chill
*/
public class JwtUtil {
/**
* token基础配置
*/
public static String BEARER = "bearer";
public static String CRYPTO = "crypto";
public static Integer AUTH_LENGTH = 7;
/**
* token保存至redis的key
*/
private static final String ACCESS_TOKEN_CACHE = "blade:token";
private static final String REFRESH_TOKEN_CACHE = "blade:refreshToken";
private static final String TOKEN_KEY = "token:state:";
/**
* jwt配置
*/
@Getter
private static JwtProperties jwtProperties;
/**
* redis工具
*/
@Getter
private static RedisTemplate<String, Object> redisTemplate;
public static void setJwtProperties(JwtProperties properties) {
if (JwtUtil.jwtProperties == null) {
JwtUtil.jwtProperties = properties;
}
}
public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
if (JwtUtil.redisTemplate == null) {
JwtUtil.redisTemplate = redisTemplate;
}
}
/**
* 签名加密
*/
public static String getBase64Security() {
return Base64.getEncoder().encodeToString(getJwtProperties().getSignKey().getBytes(StandardCharsets.UTF_8));
}
/**
* 获取请求传递的token串
*
* @param auth token
* @return String
*/
public static String getToken(String auth) {
if (isBearer(auth) || isCrypto(auth)) {
return auth.substring(AUTH_LENGTH);
}
return null;
}
/**
* 判断token类型为bearer
*
* @param auth token
* @return String
*/
public static Boolean isBearer(String auth) {
if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
String headStr = auth.substring(0, 6).toLowerCase();
return headStr.compareTo(BEARER) == 0;
}
return false;
}
/**
* 判断token类型为crypto
*
* @param auth token
* @return String
*/
public static Boolean isCrypto(String auth) {
if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
String headStr = auth.substring(0, 6).toLowerCase();
return headStr.compareTo(CRYPTO) == 0;
}
return false;
}
/**
* 解析jsonWebToken
*
* @param jsonWebToken token串
* @return Claims
*/
public static Claims parseJWT(String jsonWebToken) {
try {
SecretKey secretKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(getBase64Security()));
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(jsonWebToken)
.getPayload();
} catch (Exception ex) {
return null;
}
}
/**
* 获取保存在redis的accessToken
*
* @param tenantId 租户id
* @param userId 用户id
* @param accessToken accessToken
* @return accessToken
*/
public static String getAccessToken(String tenantId, String userId, String accessToken) {
return getAccessToken(tenantId, null, userId, accessToken);
}
/**
* 获取保存在redis的accessToken
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param accessToken accessToken
* @return accessToken
*/
public static String getAccessToken(String tenantId, String clientId, String userId, String accessToken) {
return String.valueOf(getRedisTemplate().opsForValue().get(getAccessTokenKey(tenantId, clientId, userId, accessToken)));
}
/**
* 添加accessToken至redis
*
* @param tenantId 租户id
* @param userId 用户id
* @param accessToken accessToken
* @param expire 过期时间
*/
public static void addAccessToken(String tenantId, String userId, String accessToken, int expire) {
addAccessToken(tenantId, null, userId, accessToken, expire);
}
/**
* 添加accessToken至redis
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param accessToken accessToken
* @param expire 过期时间
*/
public static void addAccessToken(String tenantId, String clientId, String userId, String accessToken, int expire) {
getRedisTemplate().delete(getAccessTokenKey(tenantId, clientId, userId, accessToken));
getRedisTemplate().opsForValue().set(getAccessTokenKey(tenantId, clientId, userId, accessToken), accessToken, expire, TimeUnit.SECONDS);
}
/**
* 删除保存在redis的accessToken
*
* @param tenantId 租户id
* @param userId 用户id
*/
public static void removeAccessToken(String tenantId, String userId) {
removeAccessToken(tenantId, userId, null);
}
/**
* 删除保存在redis的accessToken
*
* @param tenantId 租户id
* @param userId 用户id
* @param accessToken accessToken
*/
public static void removeAccessToken(String tenantId, String userId, String accessToken) {
removeAccessToken(tenantId, null, userId, accessToken);
}
/**
* 删除保存在redis的accessToken
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param accessToken accessToken
*/
public static void removeAccessToken(String tenantId, String clientId, String userId, String accessToken) {
getRedisTemplate().delete(getAccessTokenKey(tenantId, clientId, userId, accessToken));
}
/**
* 获取保存在redis的refreshToken
*
* @param tenantId 租户id
* @param userId 用户id
* @return refreshToken
*/
public static String getRefreshToken(String tenantId, String userId) {
return getRefreshToken(tenantId, null, userId, null);
}
/**
* 获取保存在redis的refreshToken
*
* @param tenantId 租户id
* @param userId 用户id
* @param refreshToken refreshToken
* @return refreshToken
*/
public static String getRefreshToken(String tenantId, String userId, String refreshToken) {
return getRefreshToken(tenantId, null, userId, refreshToken);
}
/**
* 获取保存在redis的refreshToken
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param refreshToken refreshToken
* @return accessToken
*/
public static String getRefreshToken(String tenantId, String clientId, String userId, String refreshToken) {
return String.valueOf(getRedisTemplate().opsForValue().get(getRefreshTokenKey(tenantId, clientId, userId, refreshToken)));
}
/**
* 添加refreshToken至redis
*
* @param tenantId 租户id
* @param userId 用户id
* @param refreshToken refreshToken
* @param expire 过期时间
*/
public static void addRefreshToken(String tenantId, String userId, String refreshToken, int expire) {
addRefreshToken(tenantId, null, userId, refreshToken, expire);
}
/**
* 添加refreshToken至redis
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param refreshToken refreshToken
* @param expire 过期时间
*/
public static void addRefreshToken(String tenantId, String clientId, String userId, String refreshToken, int expire) {
getRedisTemplate().delete(getRefreshTokenKey(tenantId, clientId, userId, refreshToken));
getRedisTemplate().opsForValue().set(getRefreshTokenKey(tenantId, clientId, userId, refreshToken), refreshToken, expire, TimeUnit.SECONDS);
}
/**
* 删除保存在refreshToken的token
*
* @param tenantId 租户id
* @param userId 用户id
*/
public static void removeRefreshToken(String tenantId, String userId) {
removeRefreshToken(tenantId, null, userId, null);
}
/**
* 删除保存在refreshToken的token
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
*/
public static void removeRefreshToken(String tenantId, String clientId, String userId) {
removeRefreshToken(tenantId, clientId, userId, null);
}
/**
* 删除保存在refreshToken的token
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param refreshToken refreshToken
*/
public static void removeRefreshToken(String tenantId, String clientId, String userId, String refreshToken) {
getRedisTemplate().delete(getRefreshTokenKey(tenantId, clientId, userId, refreshToken));
}
/**
* 获取accessToken索引
*
* @param tenantId 租户id
* @param userId 用户id
* @param accessToken accessToken
* @return token索引
*/
public static String getAccessTokenKey(String tenantId, String userId, String accessToken) {
return getAccessTokenKey(tenantId, null, userId, accessToken);
}
/**
* 获取accessToken索引
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param accessToken accessToken
* @return token索引
*/
public static String getAccessTokenKey(String tenantId, String clientId, String userId, String accessToken) {
return getTokenKey(ACCESS_TOKEN_CACHE, tenantId, clientId, userId, accessToken);
}
/**
* 获取refreshToken索引
*
* @param tenantId 租户id
* @param userId 用户id
* @return token索引
*/
public static String getRefreshTokenKey(String tenantId, String userId) {
return getRefreshTokenKey(tenantId, null, userId, null);
}
/**
* 获取refreshToken索引
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @return token索引
*/
public static String getRefreshTokenKey(String tenantId, String clientId, String userId) {
return getRefreshTokenKey(tenantId, clientId, userId, null);
}
/**
* 获取refreshToken索引
*
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param refreshToken refreshToken
* @return token索引
*/
public static String getRefreshTokenKey(String tenantId, String clientId, String userId, String refreshToken) {
return getTokenKey(REFRESH_TOKEN_CACHE, tenantId, clientId, userId, refreshToken);
}
/**
* 获取通用Token索引
*
* @param tokenCache 缓存名
* @param tenantId 租户id
* @param clientId 应用id
* @param userId 用户id
* @param tokenValue tokenValue
* @return token索引
*/
public static String getTokenKey(String tokenCache, String tenantId, String clientId, String userId, String tokenValue) {
String key = tenantId.concat(":").concat(tokenCache).concat("::").concat(TOKEN_KEY);
if (getJwtProperties().getSingle() || !StringUtils.hasText(tokenValue)) {
if (getJwtProperties().getSingleLevel() == SingleLevel.CLIENT && StringUtils.hasText(clientId)) {
key = key.concat(clientId).concat(":");
}
return key.concat(userId);
} else {
return key.concat(tokenValue);
}
}
}

View File

@@ -0,0 +1,69 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt.config;
import lombok.AllArgsConstructor;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.jwt.serializer.JwtRedisKeySerializer;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
/**
* Jwt配置类
*
* @author Chill
*/
@AllArgsConstructor
@AutoConfiguration(after = JwtRedisConfiguration.class)
@EnableConfigurationProperties({JwtProperties.class})
public class JwtConfiguration implements SmartInitializingSingleton {
private final JwtProperties jwtProperties;
private final RedisConnectionFactory redisConnectionFactory;
@Override
public void afterSingletonsInstantiated() {
// redisTemplate 实例化
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
JwtRedisKeySerializer redisKeySerializer = new JwtRedisKeySerializer();
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
// key 序列化
redisTemplate.setKeySerializer(redisKeySerializer);
redisTemplate.setHashKeySerializer(redisKeySerializer);
// value 序列化
redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
JwtUtil.setJwtProperties(jwtProperties);
JwtUtil.setRedisTemplate(redisTemplate);
}
}

View File

@@ -0,0 +1,61 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
/**
* RedisTemplate 配置
*
* @author Chill
*/
@Order
@EnableCaching
@AutoConfiguration(after = RedisAutoConfiguration.class)
public class JwtRedisConfiguration {
@Bean("redisCacheManager")
@ConditionalOnMissingBean(name = "redisCacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1));
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
}

View File

@@ -0,0 +1,45 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt.constant;
/**
* Jwt常量
*
* @author Chill
*/
public interface JwtConstant {
/**
* 默认key
*/
String DEFAULT_SECRET_KEY = "bladexisapowerfulmicroservicearchitectureupgradedandoptimizedfromacommercialproject";
/**
* key安全长度具体见https://tools.ietf.org/html/rfc7518#section-3.2
*/
int SECRET_KEY_LENGTH = 32;
}

View File

@@ -0,0 +1,79 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 单人模式平台枚举
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum SingleLevel {
/**
* 全平台仅可登录一人
*/
ALL("all", 1),
/**
* 各应用仅可登录一人
*/
CLIENT("client", 2),
;
/**
* 名称
*/
final String name;
/**
* 类型
*/
final int level;
/**
* 匹配枚举值
*
* @param name 名称
* @return SingleLevel
*/
public static SingleLevel of(String name) {
if (name == null) {
return null;
}
SingleLevel[] values = SingleLevel.values();
for (SingleLevel singleLevel : values) {
if (singleLevel.name.equals(name)) {
return singleLevel;
}
}
return null;
}
}

View File

@@ -0,0 +1,78 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.jwt.props;
import io.jsonwebtoken.JwtException;
import lombok.Data;
import org.springblade.core.jwt.constant.JwtConstant;
import org.springblade.core.jwt.enums.SingleLevel;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* JWT配置
*
* @author Chill
*/
@Data
@ConfigurationProperties("blade.token")
public class JwtProperties {
/**
* token是否有状态
*/
private Boolean state = Boolean.FALSE;
/**
* 是否只可同时在线一人
*/
private Boolean single = Boolean.FALSE;
/**
* 单人模式级别(ALL 全部平台只能有一个CLIENT 不同客户端只能有一个)
*/
private SingleLevel singleLevel = SingleLevel.ALL;
/**
* token签名
*/
private String signKey = "";
/**
* token密钥
*/
private String cryptoKey = "";
/**
* 获取签名规则
*/
public String getSignKey() {
if (this.signKey.length() < JwtConstant.SECRET_KEY_LENGTH) {
throw new JwtException("请配置 blade.token.sign-key 的值, 长度32位以上");
}
return this.signKey;
}
}

View File

@@ -0,0 +1,83 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.jwt.serializer;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* 将redis key序列化为字符串
*
* <p>
* spring cache中的简单基本类型直接使用 StringRedisSerializer 会有问题
* </p>
*
* @author L.cm
*/
public class JwtRedisKeySerializer implements RedisSerializer<Object> {
private final Charset charset;
private final ConversionService converter;
public JwtRedisKeySerializer() {
this(StandardCharsets.UTF_8);
}
public JwtRedisKeySerializer(Charset charset) {
Objects.requireNonNull(charset, "Charset must not be null");
this.charset = charset;
this.converter = DefaultConversionService.getSharedInstance();
}
@Override
public Object deserialize(byte[] bytes) {
// redis keys 会用到反序列化
if (bytes == null) {
return null;
}
return new String(bytes, charset);
}
@Override
public byte[] serialize(Object object) {
String key;
if (object instanceof SimpleKey) {
key = "";
} else if (object instanceof String) {
key = (String) object;
} else {
key = converter.convert(object, String.class);
}
return Objects.requireNonNull(key).getBytes(this.charset);
}
}