From 85daa7e42548bc045ca4a3f7f5c8559ca8ad78e8 Mon Sep 17 00:00:00 2001 From: smallchill Date: Tue, 12 Sep 2023 12:21:39 +0800 Subject: [PATCH] =?UTF-8?q?:tada:=203.7.0.RELEASE=20Token=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E4=BC=A0=E8=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/AuthFilter.java | 9 + .../springblade/gateway/utils/JwtCrypto.java | 203 ++++++++++++++++++ .../springblade/gateway/utils/JwtUtil.java | 21 +- 3 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/utils/JwtCrypto.java diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java index 0e5a7f0..e684c58 100644 --- a/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java @@ -21,9 +21,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springblade.core.launch.props.BladeProperties; import org.springblade.gateway.props.AuthProperties; import org.springblade.gateway.provider.AuthProvider; import org.springblade.gateway.provider.ResponseProvider; +import org.springblade.gateway.utils.JwtCrypto; import org.springblade.gateway.utils.JwtUtil; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -39,6 +41,8 @@ import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; +import static org.springblade.gateway.utils.JwtCrypto.BLADE_CRYPTO_AES_KEY; + /** * 鉴权认证 * @@ -50,6 +54,7 @@ import java.nio.charset.StandardCharsets; public class AuthFilter implements GlobalFilter, Ordered { private final AuthProperties authProperties; private final ObjectMapper objectMapper; + private final BladeProperties bladeProperties; private final AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override @@ -66,6 +71,10 @@ public class AuthFilter implements GlobalFilter, Ordered { } String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken; String token = JwtUtil.getToken(auth); + //校验 加密Token 合法性 + if (JwtUtil.isCrypto(auth)) { + token = JwtCrypto.decryptToString(token, bladeProperties.getEnvironment().getProperty(BLADE_CRYPTO_AES_KEY)); + } Claims claims = JwtUtil.parseJWT(token); if (claims == null) { return unAuth(resp, "请求未授权"); diff --git a/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtCrypto.java b/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtCrypto.java new file mode 100644 index 0000000..e8fca22 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtCrypto.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the dreamlu.net developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: Chill 庄骞 (smallchill@163.com) + */ +package org.springblade.gateway.utils; + +import lombok.SneakyThrows; +import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; +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.Objects; + +/** + * JwtCrypto + * + * @author Chill + */ +public class JwtCrypto { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + public static final String BLADE_CRYPTO_AES_KEY = "blade.token.aes-key"; + + + /** + * Base64加密 + * + * @param content 文本内容 + * @param aesTextKey 文本密钥 + * @return {String} + */ + public static String encryptToString(String content, String aesTextKey) { + return Base64Utils.encodeToString(encrypt(content, aesTextKey)); + } + + /** + * Base64加密 + * + * @param content 内容 + * @param aesTextKey 文本密钥 + * @return {String} + */ + public static String encryptToString(byte[] content, String aesTextKey) { + return Base64Utils.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(Base64Utils.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); + } + + /** + * 提供基于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]; + for (int index = 0; index < amountToPad; index++) { + pads[index] = 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; + } + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtUtil.java b/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtUtil.java index 84a1232..e1b0e4e 100644 --- a/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtUtil.java +++ b/blade-gateway/src/main/java/org/springblade/gateway/utils/JwtUtil.java @@ -17,6 +17,7 @@ package org.springblade.gateway.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import lombok.Getter; import org.springblade.core.launch.constant.TokenConstant; import org.springblade.gateway.props.JwtProperties; @@ -31,17 +32,15 @@ import java.util.Base64; public class JwtUtil { public static String BEARER = TokenConstant.BEARER; + public static String CRYPTO = TokenConstant.CRYPTO; public static Integer AUTH_LENGTH = 7; /** * jwt配置 */ + @Getter private static JwtProperties jwtProperties; - public static JwtProperties getJwtProperties() { - return jwtProperties; - } - public static void setJwtProperties(JwtProperties properties) { if (JwtUtil.jwtProperties == null) { JwtUtil.jwtProperties = properties; @@ -55,6 +54,20 @@ public class JwtUtil { return Base64.getEncoder().encodeToString(getJwtProperties().getSignKey().getBytes(StandardCharsets.UTF_8)); } + /** + * 判断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; + } + /** * 获取token串 *