Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a3f45abbe | ||
|
|
b154e57aef | ||
|
|
cf1d2cdd3a | ||
|
|
4ddb967f20 | ||
|
|
8738952484 |
@@ -0,0 +1,306 @@
|
||||
package com.aos.common.encrypt;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* 完全兼容微信所使用的AES加密方式.
|
||||
* aes的key必须是256byte长 (比如32个字符) ,可以使用AesKit.genAesKey()来生成一组key
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2019.12.26 21:39
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@UtilityClass
|
||||
public class AesUtils {
|
||||
|
||||
/**
|
||||
* 默认 16 长度
|
||||
*/
|
||||
private static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
|
||||
/**
|
||||
* MAXIMUM_CAPACITY
|
||||
*/
|
||||
private static final int MAXIMUM_CAPACITY = 1 << 30;
|
||||
|
||||
/**
|
||||
* 返回 2 的整数倍
|
||||
*
|
||||
* @param cap cap
|
||||
* @return the int
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private static final int relengh(int cap) {
|
||||
int n = cap - 1;
|
||||
n |= n >>> 1;
|
||||
n |= n >>> 2;
|
||||
n |= n >>> 4;
|
||||
n |= n >>> 8;
|
||||
n |= n >>> 16;
|
||||
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check power of 2
|
||||
*
|
||||
* @param n n
|
||||
* @return the boolean
|
||||
* @since 1.9.0
|
||||
*/
|
||||
private boolean checkPowerOf2(int n) {
|
||||
if (n <= 0) {
|
||||
return false;
|
||||
}
|
||||
int t = n & (n - 1);
|
||||
return t == 0 ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gen aes key string.
|
||||
*
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String genAesKey() {
|
||||
return RandomUtils.random(DEFAULT_INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gen aes key
|
||||
*
|
||||
* @param length length
|
||||
* @return the string
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public static String genAesKey(int length) {
|
||||
return RandomUtils.random(relengh(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt byte [ ].
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesTextKey the aes text key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] encrypt(byte[] content, String aesTextKey) {
|
||||
return encrypt(content, aesTextKey.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt byte [ ].
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesKey the aes key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] encrypt(byte[] content, byte[] aesKey) {
|
||||
Assert.isTrue(aesKey.length >= DEFAULT_INITIAL_CAPACITY && checkPowerOf2(aesKey.length),
|
||||
"密钥必须为 2 的幂次方且大于等于 16 位");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, DEFAULT_INITIAL_CAPACITY);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||
return cipher.doFinal(Pkcs7Encoder.encode(content));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt byte [ ].
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesTextKey the aes text key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] encrypt(@NotNull String content, @NotNull String aesTextKey) {
|
||||
return encrypt(content.getBytes(Charsets.UTF_8), aesTextKey.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt to str
|
||||
*
|
||||
* @param content content
|
||||
* @param aesTextKey aes text key
|
||||
* @return the string
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public static String encryptToStr(@NotNull String content, @NotNull String aesTextKey) {
|
||||
return com.fkhwl.starter.core.util.Base64Utils.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt byte [ ].
|
||||
*
|
||||
* @param content the content
|
||||
* @param charset the charset
|
||||
* @param aesTextKey the aes text key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] encrypt(@NotNull String content, java.nio.charset.Charset charset, @NotNull String aesTextKey) {
|
||||
return encrypt(content.getBytes(charset), aesTextKey.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt to str
|
||||
*
|
||||
* @param content content
|
||||
* @param aesTextKey aes text key
|
||||
* @return the string
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public static String decryptToStr(@NotNull String content, @NotNull String aesTextKey) {
|
||||
return new String(decrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt
|
||||
*
|
||||
* @param content content
|
||||
* @param aesTextKey aes text key
|
||||
* @return the byte [ ]
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public static byte[] decrypt(@NotNull String content, @NotNull String aesTextKey) {
|
||||
return decrypt(com.fkhwl.starter.core.util.Base64Utils.decodeFromString(content), aesTextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt byte [ ].
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesTextKey the aes text key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] decrypt(byte[] content, @NotNull String aesTextKey) {
|
||||
return decrypt(content, aesTextKey.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt byte [ ].
|
||||
*
|
||||
* @param encrypted the encrypted
|
||||
* @param aesKey the aes key
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
|
||||
Assert.isTrue(aesKey.length >= DEFAULT_INITIAL_CAPACITY && checkPowerOf2(aesKey.length),
|
||||
"密钥必须为 2 的幂次方且大于等于 16 位");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, DEFAULT_INITIAL_CAPACITY));
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
|
||||
return Pkcs7Encoder.decode(cipher.doFinal(encrypted));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt to str string.
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesTextKey the aes text key
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract("_, _ -> new")
|
||||
public static String decryptToStr(byte[] content, @NotNull String aesTextKey) {
|
||||
return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt to str string.
|
||||
*
|
||||
* @param content the content
|
||||
* @param aesTextKey the aes text key
|
||||
* @param charset the charset
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract("_, _, _ -> new")
|
||||
public static String decryptToStr(byte[] content, @NotNull String aesTextKey, java.nio.charset.Charset charset) {
|
||||
return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供基于PKCS7算法的加解密接口.
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2019.12.26 21:39
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@UtilityClass
|
||||
static class Pkcs7Encoder {
|
||||
/**
|
||||
* The Block size.
|
||||
*/
|
||||
static final int BLOCK_SIZE = 32;
|
||||
|
||||
/**
|
||||
* Encode byte [ ].
|
||||
*
|
||||
* @param src the src
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode byte [ ].
|
||||
*
|
||||
* @param decrypted the decrypted
|
||||
* @return the byte [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
static byte[] decode(byte[] decrypted) {
|
||||
int pad = (int) 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.aos.common.encrypt;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: Base64工具 </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2019.12.26 21:45
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Base64Utils {
|
||||
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String encode(String value) {
|
||||
return encode(value, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String encode(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
return new String(encode(val), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] encode(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return Base64.getEncoder().encode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码成String
|
||||
*
|
||||
* @param src
|
||||
* @return java.lang.String
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static String encodeToString(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return new String(encode(src), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 url编码
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] encodeUrlSafe(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return Base64.getUrlEncoder().encode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String encodeUrlSafe(String value) {
|
||||
return encodeUrlSafe(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码url返回String
|
||||
*
|
||||
* @param src
|
||||
* @return java.lang.String
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static String encodeToUrlSafeString(byte[] src) {
|
||||
return new String(encodeUrlSafe(src), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String encodeUrlSafe(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
return new String(encodeUrlSafe(val), charset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String decode(String value) {
|
||||
return Base64Utils.decode(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String decode(String value, java.nio.charset.Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
byte[] decodedValue = Base64Utils.decode(val);
|
||||
return new String(decodedValue, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String decodeUrlSafe(String value) {
|
||||
return Base64Utils.decodeUrlSafe(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static String decodeUrlSafe(String value, java.nio.charset.Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
byte[] decodedValue = Base64Utils.decodeUrlSafe(val);
|
||||
return new String(decodedValue, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 解码
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] decode(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return Base64.getDecoder().decode(src);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base64 url解码
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] decodeUrlSafe(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return Base64.getUrlDecoder().decode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 从String解码为byte
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] decodeFromString(String src) {
|
||||
if (src.isEmpty()) {
|
||||
return new byte[0];
|
||||
}
|
||||
return decode(src.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 解码返回byte
|
||||
*
|
||||
* @param src
|
||||
* @return byte[]
|
||||
* @Date 2022/12/3
|
||||
* @Author wangyl
|
||||
*/
|
||||
public static byte[] decodeFromUrlSafeString(String src) {
|
||||
return decodeUrlSafe(src.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package com.aos.common.model.context;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wangyl
|
||||
* @version V1.0
|
||||
@@ -16,7 +19,11 @@ public class SecurityUserInfo {
|
||||
*/
|
||||
private String username = "未知";
|
||||
/**
|
||||
* 用户自增id
|
||||
* 登录账号
|
||||
*/
|
||||
private String account = "";
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long id = -1L;
|
||||
/**
|
||||
@@ -27,6 +34,9 @@ public class SecurityUserInfo {
|
||||
* 手机号
|
||||
*/
|
||||
private String phone = "";
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private List<String> authorities = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
@@ -11,43 +11,65 @@ public enum WebResponse {
|
||||
*/
|
||||
WebResponse;
|
||||
|
||||
public <T> WebResult ok() {
|
||||
return new WebResult(ResultCode.SUCCEED);
|
||||
public <T> WebResult buildWebResult(ResultCodeInterface resultCode, T data, String traceId) {
|
||||
return new WebResult(resultCode, data, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult ok(ResultCodeInterface resultCode, T data) {
|
||||
return new WebResult(resultCode, data);
|
||||
return this.buildWebResult(resultCode, data, null);
|
||||
}
|
||||
|
||||
public <T> WebResult ok(ResultCodeInterface resultCode, String traceId) {
|
||||
return this.buildWebResult(resultCode, null, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult ok(ResultCodeInterface resultCode) {
|
||||
return this.buildWebResult(resultCode, null, null);
|
||||
}
|
||||
|
||||
public <T> WebResult ok(T data) {
|
||||
return new WebResult(ResultCode.SUCCEED, data);
|
||||
return this.buildWebResult(ResultCode.SUCCEED, data, null);
|
||||
}
|
||||
|
||||
public <T> WebResult error() {
|
||||
return new WebResult(ResultCode.FAILED);
|
||||
public <T> WebResult ok(String traceId) {
|
||||
return this.buildWebResult(ResultCode.SUCCEED, null, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult error(T data) {
|
||||
return new WebResult(ResultCode.FAILED, data);
|
||||
public <T> WebResult ok(T data, String traceId) {
|
||||
return this.buildWebResult(ResultCode.SUCCEED, data, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult ok() {
|
||||
return this.ok(ResultCode.SUCCEED, null);
|
||||
}
|
||||
|
||||
|
||||
public <T> WebResult error(ResultCodeInterface resultCode, T data) {
|
||||
return new WebResult(resultCode, data);
|
||||
return this.buildWebResult(resultCode, data, null);
|
||||
}
|
||||
|
||||
public <T> WebResult error(ResultCodeInterface resultCode, String traceId) {
|
||||
return this.buildWebResult(resultCode, null, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult error(ResultCodeInterface resultCode) {
|
||||
return new WebResult(resultCode);
|
||||
return this.buildWebResult(resultCode, null, null);
|
||||
}
|
||||
|
||||
public <T> WebResult error(String code, String msg) {
|
||||
return new WebResult(code, msg);
|
||||
public <T> WebResult error(T data) {
|
||||
return this.buildWebResult(ResultCode.FAILED, data, null);
|
||||
}
|
||||
|
||||
private <T> WebResult WebResponse(String code, String msg, T data) {
|
||||
return new WebResult(code, msg, data);
|
||||
public <T> WebResult error(String traceId) {
|
||||
return this.buildWebResult(ResultCode.FAILED, null, traceId);
|
||||
}
|
||||
|
||||
private <T> WebResult WebResponse(String code, String msg) {
|
||||
return new WebResult(code, msg);
|
||||
public <T> WebResult error(T data, String traceId) {
|
||||
return this.buildWebResult(ResultCode.FAILED, data, traceId);
|
||||
}
|
||||
|
||||
public <T> WebResult error() {
|
||||
return this.ok(ResultCode.FAILED, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,25 +27,52 @@ public class WebResult<T> {
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 链路追踪id
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
public WebResult() {
|
||||
|
||||
}
|
||||
|
||||
public WebResult(String code, String msg, T data) {
|
||||
public WebResult(String code, String msg, T data, String traceId) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.traceId = traceId;
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode) {
|
||||
this(resultCode, null);
|
||||
public WebResult(String code, String msg, T data) {
|
||||
this(code, msg, data, null);
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode, T data) {
|
||||
this(resultCode.getCode(), resultCode.getMsg(), data);
|
||||
public WebResult(String code, String msg, String traceId) {
|
||||
this(code, msg, null, traceId);
|
||||
}
|
||||
|
||||
public WebResult(String code, String msg) {
|
||||
this(code, msg, null);
|
||||
this(code, msg, null, null);
|
||||
}
|
||||
|
||||
public WebResult(String code, T data) {
|
||||
this(code, null, data, null);
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode, T data, String traceId) {
|
||||
this(resultCode.getCode(), resultCode.getMsg(), data, traceId);
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode, T data) {
|
||||
this(resultCode.getCode(), resultCode.getMsg(), data, null);
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode, String traceId) {
|
||||
this(resultCode, null, traceId);
|
||||
}
|
||||
|
||||
public WebResult(ResultCodeInterface resultCode) {
|
||||
this(resultCode, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import com.alibaba.ttl.TtlRunnable;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -47,4 +45,16 @@ public class TestController {
|
||||
public void test4() throws Exception {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
@GetMapping("/test5/{id}")
|
||||
@ApiOperation(value = "test5")
|
||||
public void test5(@PathVariable("id") String id) {
|
||||
System.out.println(id);
|
||||
}
|
||||
|
||||
@GetMapping("/test5/test1")
|
||||
@ApiOperation(value = "test6")
|
||||
public void test6() {
|
||||
System.out.println(111);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,621 @@
|
||||
package com.aos.mybatis.plus;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fkhwl.starter.basic.asserts.Assertions;
|
||||
import com.fkhwl.starter.basic.util.Exceptions;
|
||||
import com.fkhwl.starter.basic.util.StringPool;
|
||||
import com.fkhwl.starter.core.exception.BaseException;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cglib.core.CodeGenerationException;
|
||||
import org.springframework.core.convert.Property;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: 反射工具类</p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 18:18
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class ReflectionUtils extends org.springframework.util.ReflectionUtils {
|
||||
|
||||
/** class field cache */
|
||||
private static final Map<Class<?>, List<Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/** PRIMITIVE_WRAPPER_TYPE_MAP */
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_TYPE_MAP = new IdentityHashMap<>(8);
|
||||
|
||||
static {
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class);
|
||||
PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Bean 的所有 get方法
|
||||
*
|
||||
* @param type 类
|
||||
* @return PropertyDescriptor数组 property descriptor [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static PropertyDescriptor[] getBeanGetters(Class<?> type) {
|
||||
return getPropertiesHelper(type, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Bean 的所有 PropertyDescriptor
|
||||
*
|
||||
* @param type 类
|
||||
* @param read 读取方法
|
||||
* @param write 写方法
|
||||
* @return PropertyDescriptor数组 property descriptor [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static PropertyDescriptor[] getPropertiesHelper(Class<?> type, boolean read, boolean write) {
|
||||
try {
|
||||
PropertyDescriptor[] all = BeanUtils.getPropertyDescriptors(type);
|
||||
if (read && write) {
|
||||
return all;
|
||||
} else {
|
||||
List<PropertyDescriptor> properties = new ArrayList<>(all.length);
|
||||
for (PropertyDescriptor pd : all) {
|
||||
boolean canRead = read && pd.getReadMethod() != null;
|
||||
boolean canWrite = write && pd.getWriteMethod() != null;
|
||||
if (canRead || canWrite) {
|
||||
properties.add(pd);
|
||||
}
|
||||
}
|
||||
return properties.toArray(new PropertyDescriptor[0]);
|
||||
}
|
||||
} catch (BeansException ex) {
|
||||
throw new CodeGenerationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Bean 的所有 set方法
|
||||
*
|
||||
* @param type 类
|
||||
* @return PropertyDescriptor数组 property descriptor [ ]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static PropertyDescriptor[] getBeanSetters(Class<?> type) {
|
||||
return getPropertiesHelper(type, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 bean 的属性信息
|
||||
*
|
||||
* @param propertyType 类型
|
||||
* @param propertyName 属性名
|
||||
* @return {Property}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static TypeDescriptor getTypeDescriptor(Class<?> propertyType, String propertyName) {
|
||||
Property property = ReflectionUtils.getProperty(propertyType, propertyName);
|
||||
if (property == null) {
|
||||
return null;
|
||||
}
|
||||
return new TypeDescriptor(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 bean 的属性信息
|
||||
*
|
||||
* @param propertyType 类型
|
||||
* @param propertyName 属性名
|
||||
* @return {Property}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static Property getProperty(Class<?> propertyType, String propertyName) {
|
||||
PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(propertyType, propertyName);
|
||||
if (propertyDescriptor == null) {
|
||||
return null;
|
||||
}
|
||||
return ReflectionUtils.getProperty(propertyType, propertyDescriptor, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 bean 的属性信息
|
||||
*
|
||||
* @param propertyType 类型
|
||||
* @param propertyDescriptor PropertyDescriptor
|
||||
* @param propertyName 属性名
|
||||
* @return {Property}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static Property getProperty(Class<?> propertyType, @NotNull PropertyDescriptor propertyDescriptor, String propertyName) {
|
||||
Method readMethod = propertyDescriptor.getReadMethod();
|
||||
Method writeMethod = propertyDescriptor.getWriteMethod();
|
||||
return new Property(propertyType, readMethod, writeMethod, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 类属性信息
|
||||
*
|
||||
* @param propertyType 类型
|
||||
* @param propertyDescriptor PropertyDescriptor
|
||||
* @param propertyName 属性名
|
||||
* @return {Property}
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@NotNull
|
||||
public static TypeDescriptor getTypeDescriptor(Class<?> propertyType,
|
||||
@NotNull PropertyDescriptor propertyDescriptor,
|
||||
String propertyName) {
|
||||
Method readMethod = propertyDescriptor.getReadMethod();
|
||||
Method writeMethod = propertyDescriptor.getWriteMethod();
|
||||
Property property = new Property(propertyType, readMethod, writeMethod, propertyName);
|
||||
return new TypeDescriptor(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 所有 field 属性上的注解
|
||||
*
|
||||
* @param <T> 注解泛型
|
||||
* @param clazz 类
|
||||
* @param fieldName 属性名
|
||||
* @param annotationClass 注解
|
||||
* @return 注解 annotation
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends Annotation> T getAnnotation(Class<?> clazz, String fieldName, Class<T> annotationClass) {
|
||||
Field field = ReflectionUtils.getField(clazz, fieldName);
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
return field.getAnnotation(annotationClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 类属性
|
||||
*
|
||||
* @param clazz 类信息
|
||||
* @param fieldName 属性名
|
||||
* @return Field field
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public static Field getField(Class<?> clazz, String fieldName) {
|
||||
while (clazz != Object.class) {
|
||||
try {
|
||||
return clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射 method 方法名, 例如 getId
|
||||
*
|
||||
* @param field field
|
||||
* @param str 属性字符串内容
|
||||
* @return the method capitalize
|
||||
* @since 1.0.0
|
||||
* @deprecated 3.3.0 {@link #guessGetterName(Field, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getMethodCapitalize(@NotNull Field field, String str) {
|
||||
Class<?> fieldType = field.getType();
|
||||
return guessGetterName(str, fieldType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射 method 方法名, 例如 setVersion
|
||||
*
|
||||
* @param field Field
|
||||
* @param str String JavaBean类的version属性名
|
||||
* @return version属性的setter方法名称 , e.g. setVersion
|
||||
* @since 1.0.0
|
||||
* @deprecated 3.0.8
|
||||
*/
|
||||
@Deprecated
|
||||
public static String setMethodCapitalize(Field field, String str) {
|
||||
return concatCapitalize("set", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接字符串第二个字符串第一个字母大写
|
||||
*
|
||||
* @param concatStr concat str
|
||||
* @param str str
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static String concatCapitalize(String concatStr, String str) {
|
||||
if (StringUtils.isBlank(concatStr)) {
|
||||
concatStr = StringPool.EMPTY;
|
||||
}
|
||||
if (str == null || str.length() == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
char firstChar = str.charAt(0);
|
||||
if (Character.isTitleCase(firstChar)) {
|
||||
// already capitalized
|
||||
return str;
|
||||
}
|
||||
return concatStr + Character.toTitleCase(firstChar) + str.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取 public get方法的值
|
||||
* </p>
|
||||
*
|
||||
* @param cls ignore
|
||||
* @param entity 实体
|
||||
* @param str 属性字符串内容
|
||||
* @return Object method value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Object getMethodValue(Class<?> cls, Object entity, String str) {
|
||||
Map<String, Field> fieldMaps = getFieldMap(cls);
|
||||
try {
|
||||
Assert.notEmpty(fieldMaps, StringUtils.format("Error: NoSuchField in {} for {}. Cause:", cls.getSimpleName(), str));
|
||||
Method method = cls.getMethod(guessGetterName(fieldMaps.get(str), str));
|
||||
return method.invoke(entity);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new BaseException("Error: NoSuchMethod in %s. Cause:", e, cls.getSimpleName());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new BaseException("Error: Cannot execute a private method. in %s. Cause:", e, cls.getSimpleName());
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new BaseException("Error: InvocationTargetException on getMethodValue. Cause:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 猜测方法名
|
||||
*
|
||||
* @param field 字段
|
||||
* @param str 属性字符串内容
|
||||
* @return the string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static String guessGetterName(@NotNull Field field, String str) {
|
||||
return guessGetterName(str, field.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 猜测方法属性对应的 Getter 名称, 具体规则请参考 JavaBeans 规范
|
||||
*
|
||||
* @param name 属性名称
|
||||
* @param type 属性类型
|
||||
* @return 返回猜测的名称 string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static String guessGetterName(String name, Class<?> type) {
|
||||
return boolean.class == type ? name.startsWith("is") ? name : "is" + StrUtil.upperFirst(name) : "get" + StrUtil.upperFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 public get方法的值
|
||||
*
|
||||
* @param entity 实体
|
||||
* @param str 属性字符串内容
|
||||
* @return Object method value
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Contract("null, _ -> null")
|
||||
public static Object getMethodValue(Object entity, String str) {
|
||||
if (null == entity) {
|
||||
return null;
|
||||
}
|
||||
return getMethodValue(entity.getClass(), entity, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射对象获取泛型
|
||||
*
|
||||
* @param clazz 对象
|
||||
* @param index 泛型所在位置
|
||||
* @return Class super class generic type
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Class<?> getSuperClassGenericType(@NotNull Class<?> clazz, int index) {
|
||||
Type genType = clazz.getGenericSuperclass();
|
||||
if (!(genType instanceof ParameterizedType)) {
|
||||
log.warn("Warn: [{}] superclass not ParameterizedType", clazz.getSimpleName());
|
||||
return Object.class;
|
||||
}
|
||||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||
if (index >= params.length || index < 0) {
|
||||
log.warn("Warn: Index: [{}], Size of [{}] Parameterized Type: [{}] .", index, clazz.getSimpleName(), params.length);
|
||||
return Object.class;
|
||||
}
|
||||
if (!(params[index] instanceof Class)) {
|
||||
log.warn("Warn: [{}] not set the actual class on superclass generic parameter", clazz.getSimpleName());
|
||||
return Object.class;
|
||||
}
|
||||
return (Class<?>) params[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该类的所有属性列表
|
||||
*
|
||||
* @param clazz 反射类
|
||||
* @return the field map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Map<String, Field> getFieldMap(Class<?> clazz) {
|
||||
List<Field> fieldList = getFieldList(clazz);
|
||||
return CollectionUtils.isNotEmpty(fieldList) ? fieldList.stream()
|
||||
.collect(Collectors.toMap(Field::getName, field -> field)) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该类的所有属性列表
|
||||
*
|
||||
* @param clazz 反射类
|
||||
* @return the field list
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static List<Field> getFieldList(Class<?> clazz) {
|
||||
if (Objects.isNull(clazz)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Field> fields = CLASS_FIELD_CACHE.get(clazz);
|
||||
if (CollectionUtils.isEmpty(fields)) {
|
||||
synchronized (CLASS_FIELD_CACHE) {
|
||||
fields = doGetFieldList(clazz);
|
||||
CLASS_FIELD_CACHE.put(clazz, fields);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该类的所有属性列表
|
||||
*
|
||||
* @param clazz 反射类
|
||||
* @return the list
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull List<Field> doGetFieldList(@NotNull Class<?> clazz) {
|
||||
if (clazz.getSuperclass() != null) {
|
||||
// 排除重载属性
|
||||
Map<String, Field> fieldMap = excludeOverrideSuperField(clazz.getDeclaredFields(),
|
||||
// 处理父类字段
|
||||
getFieldList(clazz.getSuperclass()));
|
||||
List<Field> fieldList = new ArrayList<>();
|
||||
/*
|
||||
* 重写父类属性过滤后处理忽略部分, 支持过滤父类属性功能
|
||||
* 场景: 中间表不需要记录创建时间, 忽略父类 createTime 公共属性
|
||||
* 中间表实体重写父类属性 ` private transient Date createTime; `
|
||||
*/
|
||||
fieldMap.forEach((k, v) -> {
|
||||
// 过滤静态属性
|
||||
if (!Modifier.isStatic(v.getModifiers())
|
||||
// 过滤 transient关键字修饰的属性
|
||||
&& !Modifier.isTransient(v.getModifiers())) {
|
||||
fieldList.add(v);
|
||||
}
|
||||
});
|
||||
return fieldList;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序重置父类属性
|
||||
*
|
||||
* @param fields 子类属性
|
||||
* @param superFieldList 父类属性
|
||||
* @return the map
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static Map<String, Field> excludeOverrideSuperField(Field[] fields, @NotNull List<Field> superFieldList) {
|
||||
// 子类属性
|
||||
Map<String, Field> fieldMap = Stream.of(fields)
|
||||
.collect(toMap(Field::getName, identity(), (u, v) -> {
|
||||
throw new IllegalStateException(String.format("Duplicate key %s", u));
|
||||
}, LinkedHashMap::new));
|
||||
superFieldList.stream()
|
||||
.filter(field -> !fieldMap.containsKey(field.getName()))
|
||||
.forEach(f -> fieldMap.put(f.getName(), f));
|
||||
return fieldMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段get方法
|
||||
*
|
||||
* @param cls class
|
||||
* @param field 字段
|
||||
* @return Get方法 method
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @NotNull Method getMethod(@NotNull Class<?> cls, Field field) {
|
||||
try {
|
||||
return cls.getDeclaredMethod(guessGetterName(field, field.getName()));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new BaseException("Error: NoSuchMethod in %s. Cause:", e, cls.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为基本类型或基本包装类型
|
||||
*
|
||||
* @param clazz class
|
||||
* @return 是否基本类型或基本包装类型 boolean
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isPrimitiveOrWrapper(Class<?> clazz) {
|
||||
Assertions.notNull(clazz, "Class must not be null");
|
||||
return (clazz.isPrimitive() || PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的 DeclaredMethod
|
||||
*
|
||||
* @param object : 子类对象
|
||||
* @param methodName : 父类中的方法名
|
||||
* @param parameterTypes : 父类中的方法参数类型
|
||||
* @return 父类中的方法对象 declared method
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @Nullable Method getDeclaredMethod(@NotNull Object object, String methodName, Class<?>... parameterTypes) {
|
||||
Method method;
|
||||
for (Class<?> clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
|
||||
try {
|
||||
method = clazz.getDeclaredMethod(methodName, parameterTypes);
|
||||
return method;
|
||||
} catch (Exception e) {
|
||||
// 这里甚么都不要做!并且这里的异常必须这样写, 不能抛出去.
|
||||
// 如果这里的异常打印或者往外抛, 则就不会执行clazz=clazz.getSuperclass(),最后就不会进入到父类中了
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 而忽略修饰符(private, protected, default)
|
||||
*
|
||||
* @param object : 子类对象
|
||||
* @param methodName : 父类中的方法名
|
||||
* @param parameterTypes : 父类中的方法参数类型
|
||||
* @param parameters : 父类中的方法参数
|
||||
* @return 父类中方法的执行结果 object
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @Nullable Object invokeMethod(Object object,
|
||||
String methodName,
|
||||
Class<?>[] parameterTypes,
|
||||
Object[] parameters) {
|
||||
// 根据 对象、方法名和对应的方法参数 通过反射 调用上面的方法获取 Method 对象
|
||||
Method method = getDeclaredMethod(object, methodName, parameterTypes);
|
||||
try {
|
||||
if (null != method) {
|
||||
// 抑制Java对方法进行检查,主要是针对私有方法而言
|
||||
method.setAccessible(true);
|
||||
// 调用object 的 method 所代表的方法, 其方法的参数是 parameters
|
||||
return method.invoke(object, parameters);
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的 DeclaredField
|
||||
*
|
||||
* @param object : 子类对象
|
||||
* @param fieldName : 父类中的属性名
|
||||
* @return 父类中的属性对象 declared field
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @Nullable Field getDeclaredField(@NotNull Object object, String fieldName) {
|
||||
Field field;
|
||||
Class<?> clazz = object.getClass();
|
||||
for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
return field;
|
||||
} catch (Exception e) {
|
||||
// 这里甚么都不要做!并且这里的异常必须这样写, 不能抛出去.
|
||||
// 如果这里的异常打印或者往外抛, 则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter
|
||||
*
|
||||
* @param object : 子类对象
|
||||
* @param fieldName : 父类中的属性名
|
||||
* @param value : 将要设置的值
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static void setFieldValue(Object object, String fieldName, Object value) {
|
||||
// 根据 对象和属性名通过反射 调用上面的方法获取 Field对象
|
||||
Field field = getDeclaredField(object, fieldName);
|
||||
try {
|
||||
if (field != null) {
|
||||
// 抑制Java对其的检查
|
||||
field.setAccessible(true);
|
||||
// 将 object 中 field 所代表的值 设置为 value
|
||||
field.set(object, value);
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter
|
||||
*
|
||||
* @param object : 子类对象
|
||||
* @param fieldName : 父类中的属性名
|
||||
* @return : 父类中的属性值
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static @Nullable Object getFieldValue(Object object, String fieldName) {
|
||||
|
||||
// 根据 对象和属性名通过反射 调用上面的方法获取 Field对象
|
||||
Field field = getDeclaredField(object, fieldName);
|
||||
|
||||
try {
|
||||
if (field != null) {
|
||||
// 抑制Java对其的检查
|
||||
field.setAccessible(true);
|
||||
// 获取 object 中 field 所代表的属性值
|
||||
return field.get(object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter
|
||||
*
|
||||
* @param <T> parameter
|
||||
* @param object object
|
||||
* @param fieldName field name
|
||||
* @param type type
|
||||
* @return the field value
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static @Nullable <T> T getFieldValue(Object object, String fieldName, Class<T> type) {
|
||||
Object fieldValue = getFieldValue(object, fieldName);
|
||||
return fieldValue != null ? (T) fieldValue : null;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -2,10 +2,15 @@ package com.aos.mybatis.plus.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2022/12/2
|
||||
* @description: 标记字段需要解密
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DecryptTransaction {
|
||||
public @interface DecryptFiled {
|
||||
|
||||
}
|
||||
+4
-2
@@ -3,11 +3,13 @@ package com.aos.mybatis.plus.annotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 加密
|
||||
* @author: wangyl
|
||||
* @date: 2022/12/2
|
||||
* @description: 标记字段需要加密
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EncryptTransaction {
|
||||
public @interface EncryptFiled {
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package com.aos.mybatis.plus.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2022/12/2
|
||||
* @description: 标记实体需要加密或者解密或者脱敏
|
||||
*/
|
||||
@Inherited
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package com.aos.mybatis.plus.interceptor;
|
||||
|
||||
import com.aos.mybatis.plus.annotation.EncryptFiled;
|
||||
import com.aos.mybatis.plus.annotation.SensitiveData;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2022/12/3
|
||||
* @description: 更新插入加密
|
||||
*/
|
||||
@Slf4j
|
||||
@Intercepts({
|
||||
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
|
||||
})
|
||||
public class AosSensitiveFieldEncryptInterceptor implements Interceptor {
|
||||
/**
|
||||
* 加密私钥
|
||||
*/
|
||||
private final String sensitiveKey;
|
||||
private final String PARAMETER = "parameterObject";
|
||||
|
||||
public AosSensitiveFieldEncryptInterceptor(String sensitiveKey) {
|
||||
this.sensitiveKey = sensitiveKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
|
||||
/**
|
||||
*获取参数对像,即 mapper 中 paramsType 的实例
|
||||
*/
|
||||
Field parameterField = parameterHandler.getClass().getDeclaredField(PARAMETER);
|
||||
parameterField.setAccessible(true);
|
||||
/**
|
||||
* 获取字段的值
|
||||
*/
|
||||
Object parameterObject = parameterField.get(parameterHandler);
|
||||
if (parameterObject != null) {
|
||||
/**
|
||||
* 获取对象的类
|
||||
*/
|
||||
Class<?> clazz = parameterObject.getClass();
|
||||
if (!clazz.getSuperclass().isInstance(Object.class)) {
|
||||
/**
|
||||
* 判断父类是否有相关注解
|
||||
*/
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
SensitiveData annotation = AnnotationUtils.findAnnotation(superclass, SensitiveData.class);
|
||||
if (Objects.nonNull(annotation)) {
|
||||
// this.encryptField(superclass.getDeclaredFields(), o, sqlCommandType);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 校验本身是否有相关注解
|
||||
*/
|
||||
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(clazz, SensitiveData.class);
|
||||
if (Objects.nonNull(sensitiveData)) {
|
||||
//取出当前当前类所有字段,传入加密方法
|
||||
Field[] declaredFields = parameterObjectClass.getDeclaredFields();
|
||||
encrypt.encrypt(declaredFields, parameterObject);
|
||||
}
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
|
||||
}
|
||||
|
||||
private void encryptField(Class<?> clazz, Object parameter) {
|
||||
SensitiveData annotation = AnnotationUtils.findAnnotation(clazz, SensitiveData.class);
|
||||
if (Objects.nonNull(annotation)) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(EncryptFiled.class)) {
|
||||
// 如果使用了指定注解, 对内容加密再存储
|
||||
Object fieldValue = ReflectionUtils.getFieldValue(parameter, field.getName());
|
||||
if (!StringUtils.isEmpty(fieldValue)) {
|
||||
byte[] encrypt = AesUtils.encrypt(String.valueOf(fieldValue), this.sensitiveKey);
|
||||
String encryptStr = Base64Utils.encodeToString(encrypt);
|
||||
ReflectionUtils.setFieldValue(parameter, field.getName(), encryptStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package com.aos.mybatis.plus.interceptor;
|
||||
|
||||
import com.aos.mybatis.plus.Encrypt;
|
||||
import com.aos.mybatis.plus.annotation.SensitiveData;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* com.oak.mybatisplus.interceptor -> sql查询参数加密
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Intercepts({
|
||||
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
|
||||
})
|
||||
public class ParameterInterceptor implements Interceptor {
|
||||
|
||||
|
||||
@Autowired
|
||||
Encrypt encrypt;
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
//@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler
|
||||
//若指定ResultSetHandler ,这里则能强转为ResultSetHandler
|
||||
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
|
||||
// 获取参数对像,即 mapper 中 paramsType 的实例
|
||||
Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
|
||||
parameterField.setAccessible(true);
|
||||
//取出实例
|
||||
Object parameterObject = parameterField.get(parameterHandler);
|
||||
if (parameterObject != null) {
|
||||
Class<?> parameterObjectClass = parameterObject.getClass();
|
||||
//校验该实例的类是否被@SensitiveData所注解
|
||||
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
|
||||
if (Objects.nonNull(sensitiveData)) {
|
||||
//取出当前当前类所有字段,传入加密方法
|
||||
Field[] declaredFields = parameterObjectClass.getDeclaredFields();
|
||||
encrypt.encrypt(declaredFields, parameterObject);
|
||||
}
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
package com.aos.web.advice;
|
||||
|
||||
import com.aos.common.model.exception.AosBaseBizException;
|
||||
import com.aos.common.model.result.WebResponse;
|
||||
import com.aos.common.model.result.WebResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2022/11/14
|
||||
@@ -17,10 +24,30 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@Slf4j
|
||||
public class ControllerErrorResponseAdvice {
|
||||
|
||||
@ExceptionHandler(AosBaseBizException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public WebResult<?> handleAosBaseBizException(AosBaseBizException ex) {
|
||||
log.error("AosBaseBizException", ex);
|
||||
return WebResponse.WebResponse.error(ex.getErrorCode(), ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public WebResult<?> handleException(MethodArgumentNotValidException ex) {
|
||||
log.error("MethodArgumentNotValidException", ex);
|
||||
return this.handleError(ex.getBindingResult());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public WebResult<?> handleException(Exception ex) {
|
||||
log.error("Exception", ex);
|
||||
return WebResponse.WebResponse.error();
|
||||
}
|
||||
|
||||
private WebResult<?> handleError(BindingResult bindingResult) {
|
||||
FieldError fieldError = bindingResult.getFieldError();
|
||||
String message = Optional.ofNullable(fieldError).map(DefaultMessageSourceResolvable::getDefaultMessage).orElse("参数不正确");
|
||||
return WebResponse.WebResponse.error(message);
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class AosJacksonBuilderCustomizer implements Jackson2ObjectMapperBuilderC
|
||||
* Long类型的序列化/反序列化
|
||||
*/
|
||||
jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
|
||||
jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
|
||||
// jacksonObjectMapperBuilder.serializerByType(Long.TYPE, ToStringSerializer.instance);
|
||||
jacksonObjectMapperBuilder.deserializerByType(Long.TYPE, new NumberDeserializers.LongDeserializer(Long.TYPE, 0L));
|
||||
jacksonObjectMapperBuilder.deserializerByType(Long.class, new NumberDeserializers.LongDeserializer(Long.class, null));
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user