第一次提交
This commit is contained in:
114
fastbee-common/src/main/java/com/fastbee/common/utils/Arith.java
Normal file
114
fastbee-common/src/main/java/com/fastbee/common/utils/Arith.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 精确的浮点数运算
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Arith
|
||||
{
|
||||
|
||||
/** 默认除法运算精度 */
|
||||
private static final int DEF_DIV_SCALE = 10;
|
||||
|
||||
/** 这个类不能实例化 */
|
||||
private Arith()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的加法运算。
|
||||
* @param v1 被加数
|
||||
* @param v2 加数
|
||||
* @return 两个参数的和
|
||||
*/
|
||||
public static double add(double v1, double v2)
|
||||
{
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.add(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的减法运算。
|
||||
* @param v1 被减数
|
||||
* @param v2 减数
|
||||
* @return 两个参数的差
|
||||
*/
|
||||
public static double sub(double v1, double v2)
|
||||
{
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.subtract(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的乘法运算。
|
||||
* @param v1 被乘数
|
||||
* @param v2 乘数
|
||||
* @return 两个参数的积
|
||||
*/
|
||||
public static double mul(double v1, double v2)
|
||||
{
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
return b1.multiply(b2).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
|
||||
* 小数点以后10位,以后的数字四舍五入。
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2)
|
||||
{
|
||||
return div(v1, v2, DEF_DIV_SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
|
||||
* 定精度,以后的数字四舍五入。
|
||||
* @param v1 被除数
|
||||
* @param v2 除数
|
||||
* @param scale 表示表示需要精确到小数点以后几位。
|
||||
* @return 两个参数的商
|
||||
*/
|
||||
public static double div(double v1, double v2, int scale)
|
||||
{
|
||||
if (scale < 0)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||
if (b1.compareTo(BigDecimal.ZERO) == 0)
|
||||
{
|
||||
return BigDecimal.ZERO.doubleValue();
|
||||
}
|
||||
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供精确的小数位四舍五入处理。
|
||||
* @param v 需要四舍五入的数字
|
||||
* @param scale 小数点后保留几位
|
||||
* @return 四舍五入后的结果
|
||||
*/
|
||||
public static double round(double v, int scale)
|
||||
{
|
||||
if (scale < 0)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"The scale must be a positive integer or zero");
|
||||
}
|
||||
BigDecimal b = new BigDecimal(Double.toString(v));
|
||||
BigDecimal one = BigDecimal.ONE;
|
||||
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author fastb
|
||||
* @version 1.0
|
||||
* @description: TODO
|
||||
* @date 2023-12-26 9:27
|
||||
*/
|
||||
public class Base64ToMultipartFile implements MultipartFile {
|
||||
private final byte[] fileContent;
|
||||
|
||||
private final String extension;
|
||||
private final String contentType;
|
||||
|
||||
|
||||
/**
|
||||
* @param base64
|
||||
* @param dataUri 格式类似于: data:image/png;base64
|
||||
*/
|
||||
public Base64ToMultipartFile(String base64, String dataUri) {
|
||||
this.fileContent = Base64.getDecoder().decode(base64.getBytes(StandardCharsets.UTF_8));
|
||||
this.extension = dataUri.split(";")[0].split("/")[1];
|
||||
this.contentType = dataUri.split(";")[0].split(":")[1];
|
||||
}
|
||||
|
||||
public Base64ToMultipartFile(String base64, String extension, String contentType) {
|
||||
this.fileContent = Base64.getDecoder().decode(base64.getBytes(StandardCharsets.UTF_8));
|
||||
this.extension = extension;
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "param_" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalFilename() {
|
||||
return "file_" + System.currentTimeMillis() + "." + extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return fileContent == null || fileContent.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return fileContent.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws IOException {
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(fileContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(File file) throws IOException, IllegalStateException {
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(fileContent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BeanMapUtilByReflect {
|
||||
|
||||
/**
|
||||
* 对象转Map
|
||||
* @param object
|
||||
* @return
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
public static Map beanToMap(Object object) throws IllegalAccessException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
map.put(field.getName(), field.get(object));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* bean转item对象
|
||||
* @param object
|
||||
* @return
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
public static List<ThingsModelSimpleItem> beanToItem(Object object) throws IllegalAccessException {
|
||||
List<ThingsModelSimpleItem> result = new ArrayList<>();
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
ThingsModelSimpleItem item = new ThingsModelSimpleItem();
|
||||
item.setId(field.getName());
|
||||
item.setValue(field.get(object)+"");
|
||||
item.setTs(DateUtils.getNowDate());
|
||||
result.add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* map转对象
|
||||
* @param map
|
||||
* @param beanClass
|
||||
* @param <T>
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
|
||||
T object = beanClass.newInstance();
|
||||
Field[] fields = object.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
int mod = field.getModifiers();
|
||||
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
if (map.containsKey(field.getName())) {
|
||||
field.set(object, map.get(field.getName()));
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* @description 位运算工具
|
||||
* 用途:将二进制数中的每位数字1或0代表着某种开关标记,1为是,0为否,则一个数字可以代表N位的开关标记值,可有效减少过多的变量定义 或 过多的表字段
|
||||
*/
|
||||
public class BitUtils {
|
||||
|
||||
/**
|
||||
* 获取二进制数字中指定位数的结果,如:1011,指定第2位,则结果是0,第3位,则结果是1
|
||||
*
|
||||
* @param num 二进制数(可以十进制数传入,也可使用0b开头的二进制数表示形式)
|
||||
* @param bit 位数(第几位,从右往左,从0开始)
|
||||
* @return
|
||||
*/
|
||||
public static int getBitFlag(long num, int bit) {
|
||||
return (int) num >> bit & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新二进制数字中指定位的值
|
||||
*
|
||||
* @param num 二进制数(可以十进制数传入,也可使用0b开头的二进制数表示形式)
|
||||
* @param bit 位数(第几位,从右往左,从0开始)
|
||||
* @param flagValue 位标记值(true=1,false=0)
|
||||
* @return
|
||||
*/
|
||||
public static long updateBitValue(long num, int bit, boolean flagValue) {
|
||||
if (flagValue) {
|
||||
//将某位由0改为1
|
||||
return num | (1 << bit);
|
||||
} else {
|
||||
//将某位由1改为0
|
||||
return num ^ (getBitFlag(num, bit) << bit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字转换为二制值形式字符串
|
||||
*
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static String toBinaryString(long num) {
|
||||
return Long.toBinaryString(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断10进制数,某位是0还是1
|
||||
* @param num
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static int deter(int num, int i) {
|
||||
// 先将数字右移指定第i位,然后再用&与1运算
|
||||
return num >> (i-1) & 1;
|
||||
}
|
||||
|
||||
public static String bin2hex(String input) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len = input.length();
|
||||
System.out.println("原数据长度:" + (len / 8) + "字节");
|
||||
|
||||
for (int i = 0; i < len / 4; i++){
|
||||
//每4个二进制位转换为1个十六进制位
|
||||
String temp = input.substring(i * 4, (i + 1) * 4);
|
||||
int tempInt = Integer.parseInt(temp, 2);
|
||||
String tempHex = Integer.toHexString(tempInt).toUpperCase();
|
||||
sb.append(tempHex);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
public static int bin2Dec(String binaryString){
|
||||
int sum = 0;
|
||||
for(int i = 0;i < binaryString.length();i++){
|
||||
char ch = binaryString.charAt(i);
|
||||
if(ch > '2' || ch < '0')
|
||||
throw new NumberFormatException(String.valueOf(i));
|
||||
sum = sum * 2 + (binaryString.charAt(i) - '0');
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static int[] string2Ins(String input) {
|
||||
StringBuilder in = new StringBuilder(input);
|
||||
int remainder = in.length() % 8;
|
||||
if (remainder > 0)
|
||||
for (int i = 0; i < 8 - remainder; i++)
|
||||
in.append("0");
|
||||
int[] result = new int[in.length() /8];
|
||||
|
||||
// Step 8 Apply compression
|
||||
for (int i = 0; i < result.length; i++)
|
||||
result[i] = Integer.parseInt(in.substring(i * 8, i * 8 + 8), 2);
|
||||
|
||||
return result;
|
||||
}
|
||||
public static byte[] string2bytes(String input) {
|
||||
StringBuilder in = new StringBuilder(input);
|
||||
int remainder = in.length() % 8;
|
||||
if (remainder > 0)
|
||||
for (int i = 0; i < 8 - remainder; i++)
|
||||
in.insert(0,"0");
|
||||
byte[] bts = new byte[in.length() / 8];
|
||||
|
||||
// Step 8 Apply compression
|
||||
for (int i = 0; i < bts.length; i++)
|
||||
bts[i] = (byte) Integer.parseInt(in.substring(i * 8, i * 8 + 8), 2);
|
||||
|
||||
return bts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得十制数组的from~to位,并按照十六进制转化值
|
||||
*
|
||||
* @param data
|
||||
* @param from
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
private static String getOctFromHexBytes(byte[] data, Object from, Object... to) {
|
||||
if (data != null && data.length > 0 && from != null) {
|
||||
try {
|
||||
byte[] value;
|
||||
int fromIndex = Integer.parseInt(from.toString());
|
||||
if (to != null && to.length > 0) {
|
||||
int toIndex = Integer.parseInt(to[0].toString());
|
||||
if (fromIndex >= toIndex || toIndex <= 0) {
|
||||
value = Arrays.copyOfRange(data, fromIndex, fromIndex + 1);
|
||||
} else {
|
||||
value = Arrays.copyOfRange(data, fromIndex, toIndex + 1);
|
||||
}
|
||||
} else {
|
||||
value = Arrays.copyOfRange(data, fromIndex, fromIndex + 1);
|
||||
}
|
||||
if (value != null && value.length > 0) {
|
||||
long octValue = 0L;
|
||||
int j = -1;
|
||||
for (int i = value.length - 1; i >= 0; i--, j++) {
|
||||
int d = value[i];
|
||||
if (d < 0) {
|
||||
d += 256;
|
||||
}
|
||||
octValue += Math.round(d * Math.pow(16, 2 * j + 2));
|
||||
}
|
||||
return new Long(octValue).toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 十进制的字符串表示转成字节数组
|
||||
*
|
||||
* @param octString
|
||||
* 十进制格式的字符串
|
||||
* @param capacity
|
||||
* 需要填充的容量(可选)
|
||||
* @return 转换后的字节数组
|
||||
**/
|
||||
private static byte[] octInt2ByteArray(Integer oct, int... capacity) {
|
||||
return hexString2ByteArray(Integer.toHexString(oct), capacity);
|
||||
}
|
||||
/**
|
||||
* 16进制的字符串表示转成字节数组
|
||||
*
|
||||
* @param hexString
|
||||
* 16进制格式的字符串
|
||||
* @param capacity
|
||||
* 需要填充的容量(可选)
|
||||
* @return 转换后的字节数组
|
||||
**/
|
||||
private static byte[] hexString2ByteArray(String hexString, int... capacity) {
|
||||
hexString = hexString.toLowerCase();
|
||||
if (hexString.length() % 2 != 0) {
|
||||
hexString = "0" + hexString;
|
||||
}
|
||||
int length = hexString.length() / 2;
|
||||
if (length < 1) {
|
||||
length = 1;
|
||||
}
|
||||
int size = length;
|
||||
if (capacity != null && capacity.length > 0 && capacity[0] >= length) {
|
||||
size = capacity[0];
|
||||
}
|
||||
final byte[] byteArray = new byte[size];
|
||||
int k = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i < size - length) {
|
||||
byteArray[i] = 0;
|
||||
} else {
|
||||
byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
|
||||
if (k + 1 < hexString.length()) {
|
||||
byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
|
||||
byteArray[i] = (byte) (high << 4 | low);
|
||||
} else {
|
||||
byteArray[i] = (byte) (high);
|
||||
}
|
||||
k += 2;
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接字节流
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static byte[] append(byte[] datas, byte[] data) {
|
||||
if (datas == null) {
|
||||
return data;
|
||||
}
|
||||
if (data == null) {
|
||||
return datas;
|
||||
} else {
|
||||
return concat(datas, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节流拼接
|
||||
*
|
||||
* @param data
|
||||
* 字节流
|
||||
* @return 拼接后的字节数组
|
||||
**/
|
||||
private static byte[] concat(byte[]... data) {
|
||||
if (data != null && data.length > 0) {
|
||||
int size = 0;
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
size += data[i].length;
|
||||
}
|
||||
byte[] byteArray = new byte[size];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
byte[] b = data[i];
|
||||
for (int j = 0; j < b.length; j++) {
|
||||
byteArray[pos++] = b[j];
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] hexStringToByteArray(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i+1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String s = bin2hex("1111111000000000");
|
||||
int i = bin2Dec("1111111000000000");
|
||||
byte[] ints = string2bytes("111111000000000");
|
||||
System.out.println(s);
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.fastbee.common.enums.ModbusDataType;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字符串公式计算工具
|
||||
*/
|
||||
public class CaculateUtils {
|
||||
|
||||
/**
|
||||
* /*
|
||||
* 暂时只支持加减乘除及括号的应用
|
||||
*/
|
||||
private static final String symbol = "+-,*/,(),%";
|
||||
|
||||
|
||||
/**
|
||||
* 公式计算 字符串
|
||||
*
|
||||
* @param exeStr
|
||||
*/
|
||||
public static BigDecimal execute(String exeStr, Map<String, String> replaceMap) {
|
||||
//替换掉占位符
|
||||
exeStr = caculateReplace(exeStr, replaceMap);
|
||||
exeStr = exeStr.replaceAll("\\s*", "");
|
||||
List<String> suffixList = suffixHandle(exeStr);
|
||||
return caculateAnalyse(suffixList);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 公式计算 后序list
|
||||
*
|
||||
* @param suffixList
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal caculateAnalyse(List<String> suffixList) {
|
||||
|
||||
BigDecimal a = BigDecimal.ZERO;
|
||||
BigDecimal b = BigDecimal.ZERO;
|
||||
// 构建一个操作数栈 每当获得操作符号时取出最上面两个数进行计算。
|
||||
Stack<BigDecimal> caculateStack = new Stack<BigDecimal>();
|
||||
if (suffixList.size() > 1) {
|
||||
|
||||
for (int i = 0; i < suffixList.size(); i++) {
|
||||
String temp = suffixList.get(i);
|
||||
if (symbol.contains(temp)) {
|
||||
b = caculateStack.pop();
|
||||
a = caculateStack.pop();
|
||||
a = caculate(a, b, temp.toCharArray()[0]);
|
||||
caculateStack.push(a);
|
||||
} else {
|
||||
if (isNumber(suffixList.get(i))) {
|
||||
caculateStack.push(new BigDecimal(suffixList.get(i)));
|
||||
} else {
|
||||
throw new RuntimeException("公式异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (suffixList.size() == 1) {
|
||||
String temp = suffixList.get(0);
|
||||
if (isNumber(temp)) {
|
||||
a = BigDecimal.valueOf(Double.parseDouble(temp));
|
||||
} else {
|
||||
throw new RuntimeException("公式异常!");
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算 使用double 进行计算 如果需要可以在这里使用bigdecimal 进行计算
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param symbol
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal caculate(BigDecimal a, BigDecimal b, char symbol) {
|
||||
switch (symbol) {
|
||||
case '+': {
|
||||
return a.add(b).stripTrailingZeros();
|
||||
}
|
||||
case '-':
|
||||
return a.subtract(b).stripTrailingZeros();
|
||||
case '*':
|
||||
return a.multiply(b);
|
||||
case '/':
|
||||
return a.divide(b);
|
||||
case '%':
|
||||
// 取余,如果不包含小数点,下面length处理会报错,这里做个处理
|
||||
if (!b.toString().contains(".0")) {
|
||||
b = new BigDecimal(b + ".0");
|
||||
}
|
||||
int length = b.toString().split("\\.")[1].length();
|
||||
return a.divide(b, length, BigDecimal.ROUND_HALF_UP);
|
||||
default:
|
||||
throw new RuntimeException("操作符号异常!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串直接 转 后序
|
||||
*/
|
||||
public static List<String> suffixHandle(String exeStr) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Stack stack = new Stack();
|
||||
char[] exeChars = exeStr.toCharArray();
|
||||
List<String> res = new ArrayList<String>();
|
||||
for (char x : exeChars) {
|
||||
// 判断是不是操作符号
|
||||
if (symbol.indexOf(x) > -1) {
|
||||
// 不管怎样先将数据添加进列表
|
||||
if (buf.length() > 0) {
|
||||
// 添加数据到res
|
||||
String temp = buf.toString();
|
||||
// 验证是否为数
|
||||
if (!isNumber(temp)) throw new RuntimeException(buf.append(" 格式不对").toString());
|
||||
|
||||
// 添加到结果列表中
|
||||
res.add(temp);
|
||||
// 清空临时buf
|
||||
buf.delete(0, buf.length());
|
||||
}
|
||||
if (stack.size() > 0) {
|
||||
|
||||
//2.判断是不是开是括号
|
||||
if (x == '(') {
|
||||
stack.push(x);
|
||||
continue;
|
||||
}
|
||||
//3.判断是不是闭合括号
|
||||
if (x == ')') {
|
||||
while (stack.size() > 0) {
|
||||
char con = (char) stack.peek();
|
||||
if (con == '(') {
|
||||
stack.pop();
|
||||
continue;
|
||||
} else {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 取出最后最近的一个操作符
|
||||
char last = (char) stack.peek();
|
||||
if (compare(x, last) > 0) {
|
||||
stack.push(x);
|
||||
} else if (compare(x, last) <= 0) {
|
||||
if (last != '(') {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
}
|
||||
stack.push(x);
|
||||
}
|
||||
} else {
|
||||
stack.push(x);
|
||||
}
|
||||
} else {
|
||||
buf.append(x);
|
||||
}
|
||||
}
|
||||
if (buf.length() > 0) res.add(buf.toString());
|
||||
while (stack.size() > 0) {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比较两个操作符号的优先级
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static int compare(char a, char b) {
|
||||
if (symbol.indexOf(a) - symbol.indexOf(b) > 1) {
|
||||
return 1;
|
||||
} else if (symbol.indexOf(a) - symbol.indexOf(b) < -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否为数 字符串
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isNumber(String str) {
|
||||
Pattern pattern = Pattern.compile("[0-9]+\\.{0,1}[0-9]*");
|
||||
Matcher isNum = pattern.matcher(str);
|
||||
return isNum.matches();
|
||||
}
|
||||
|
||||
public static String caculateReplace(String str, Map<String, String> map) {
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
str = str.replaceAll(entry.getKey(), entry.getValue()==null ? "1" : entry.getValue());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String toFloat(byte[] bytes) throws IOException {
|
||||
ByteArrayInputStream mByteArrayInputStream = new ByteArrayInputStream(bytes);
|
||||
DataInputStream mDataInputStream = new DataInputStream(mByteArrayInputStream);
|
||||
try {
|
||||
float v = mDataInputStream.readFloat();
|
||||
return String.format("%.6f",v);
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("modbus16转浮点数错误");
|
||||
}
|
||||
finally {
|
||||
mDataInputStream.close();
|
||||
mByteArrayInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String handleToUnSign16(String value,String dataType){
|
||||
long l = Long.parseLong(value);
|
||||
if (dataType.equals(ModbusDataType.U_SHORT.getType())){
|
||||
return toUnSign16(l);
|
||||
}else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转16位无符号整形
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign16(long value) {
|
||||
long unSigned = value & 0xFFFF;
|
||||
return unSigned +""; // 将字节数组转换为十六进制字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位有符号CDAB数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toSign32_CDAB(long value) {
|
||||
byte[] bytes = intToBytes2((int) value);
|
||||
return bytesToInt2(bytes)+"";
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位无符号ABCD数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign32_ABCD(long value) {
|
||||
return Integer.toUnsignedString((int) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位无符号CDAB数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign32_CDAB(long value) {
|
||||
byte[] bytes = intToBytes2((int) value);
|
||||
int val = bytesToInt2(bytes);
|
||||
return Integer.toUnsignedString(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转32位浮点数 ABCD
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static float toFloat32_ABCD(byte[] bytes) {
|
||||
int intValue = (bytes[0] << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
|
||||
return Float.intBitsToFloat(intValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转32位浮点数 CDAB
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static Float toFloat32_CDAB(byte[] bytes) {
|
||||
int intValue = ((bytes[2] & 0xFF) << 24) | ((bytes[3] & 0xFF) << 16) | ((bytes[0] & 0xFF) << 8) | ((bytes[1] & 0xFF)) ;
|
||||
return Float.intBitsToFloat(intValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* MODBUS数据类型转换
|
||||
*
|
||||
* @param dataType 数据类型
|
||||
* @param hexString
|
||||
* @return
|
||||
*/
|
||||
public static String parseValue(String dataType, String hexString) {
|
||||
String value = "";
|
||||
Long val = Long.parseLong(hexString, 16);
|
||||
byte[] bytes = ByteBufUtil.decodeHexDump(hexString);
|
||||
if (StringUtils.isNotEmpty(dataType)) {
|
||||
ModbusDataType type = ModbusDataType.convert(dataType);
|
||||
switch (type) {
|
||||
case U_SHORT:
|
||||
value = CaculateUtils.toUnSign16(val);
|
||||
break;
|
||||
case SHORT:
|
||||
case LONG_ABCD:
|
||||
value = val + "";
|
||||
break;
|
||||
case LONG_CDAB:
|
||||
value = CaculateUtils.toSign32_CDAB(val);
|
||||
break;
|
||||
case U_LONG_ABCD:
|
||||
value = CaculateUtils.toUnSign32_ABCD(val);
|
||||
break;
|
||||
case U_LONG_CDAB:
|
||||
value = CaculateUtils.toUnSign32_CDAB(val);
|
||||
break;
|
||||
case FLOAT_ABCD:
|
||||
value = CaculateUtils.toFloat32_ABCD(bytes) + "";
|
||||
break;
|
||||
case FLOAT_CDAB:
|
||||
value = CaculateUtils.toFloat32_CDAB(bytes) + "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用
|
||||
*/
|
||||
public static int bytesToInt2(byte[] src) {
|
||||
return (((src[2] & 0xFF) << 24) | ((src[3] & 0xFF) << 16) | ((src[0] & 0xFF) << 8) | (src[1] & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用
|
||||
*/
|
||||
public static byte[] intToBytes2(int value) {
|
||||
byte[] src = new byte[4];
|
||||
src[0] = (byte) ((value >> 24) & 0xFF);
|
||||
src[1] = (byte) ((value >> 16) & 0xFF);
|
||||
src[2] = (byte) ((value >> 8) & 0xFF);
|
||||
src[3] = (byte) (value & 0xFF);
|
||||
return src;
|
||||
}
|
||||
|
||||
public static String subHexValue(String hexString){
|
||||
//截取报文中的值
|
||||
String substring = hexString.substring(4, 6);
|
||||
int index = Integer.parseInt(substring);
|
||||
return hexString.substring(6, 6 + index*2);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Map<String, String> replaceMap = new HashMap<>();
|
||||
replaceMap.put("A", "1.5");
|
||||
replaceMap.put("B", "2.5");
|
||||
replaceMap.put("C", "3.5");
|
||||
replaceMap.put("D", "4.5");
|
||||
replaceMap.put("E", "10");
|
||||
// replaceMap.put("%s", "1");
|
||||
// replaceMap.put("%s1", "5");
|
||||
BigDecimal execute = execute("A + B * (C - D) % E", replaceMap);
|
||||
System.out.println(execute);
|
||||
|
||||
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("%s", "10");
|
||||
String caculate = caculateReplace("%s*2", map);
|
||||
System.out.println(caculate);
|
||||
System.out.println(execute("%s%3.00",map));
|
||||
|
||||
String s4 = toUnSign16(-1);
|
||||
System.out.println("转16位无符号:"+s4);
|
||||
|
||||
String s1 = toSign32_CDAB(40100);
|
||||
System.out.println("转32位有符号-CDAB序"+s1);
|
||||
|
||||
String s2 = toUnSign32_ABCD(-10);
|
||||
System.out.println("转32位无符号-ABCD序:"+s2);
|
||||
|
||||
String s3 = toUnSign32_CDAB(123456789);
|
||||
System.out.println("转32位无符号-CDAB序:"+s3);
|
||||
|
||||
String hexToBytes = "3fea3d71";
|
||||
byte[] bytes = ByteBufUtil.decodeHexDump(hexToBytes);
|
||||
|
||||
float v1 = toFloat32_ABCD(bytes);
|
||||
System.out.println("转32位浮点型-ABCD序:"+v1);
|
||||
|
||||
String hexToBytes1= "800041EE";
|
||||
long i = Long.parseLong(hexToBytes1, 16);
|
||||
System.out.println(i);
|
||||
byte[] bytes1 = ByteBufUtil.decodeHexDump(hexToBytes1);
|
||||
|
||||
float v2 = toFloat32_CDAB(bytes1);
|
||||
System.out.println("转32位浮点型-CDAB序:"+v2);
|
||||
|
||||
int signedShort = -32627; // 16位有符号整形
|
||||
// 将有符号短整型转换为无符号短整型
|
||||
int unSignedInt = signedShort & 0xFFFF;
|
||||
// 输出结果
|
||||
System.out.println(unSignedInt); // 输出: 0
|
||||
|
||||
long l = Long.parseLong("00501F40", 16);
|
||||
System.out.println(l);
|
||||
|
||||
int val1 = -6553510;
|
||||
byte[] bytes2 = intToBytes2(val1);
|
||||
int i1 = bytesToInt2(bytes2);
|
||||
System.out.println(i1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字符串公式计算工具
|
||||
*/
|
||||
public class CaculateVariableAndNumberUtils {
|
||||
|
||||
/**
|
||||
* /*
|
||||
* 暂时只支持加减乘除及括号的应用
|
||||
*/
|
||||
private static final String symbol = "+-,*/,(),%";
|
||||
|
||||
private static final Map<String, Integer> symbol_map = new HashMap<String, Integer>(){{
|
||||
put("*", 1);
|
||||
put("/", 1);
|
||||
put("%", 1);
|
||||
put("+", 2);
|
||||
put("-", 2);
|
||||
put("(", 3);
|
||||
put(")", 3);
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* 公式计算 字符串
|
||||
*
|
||||
* @param exeStr
|
||||
*/
|
||||
public static BigDecimal execute(String exeStr, Map<String, String> replaceMap) {
|
||||
List<String> list = suffixHandle(exeStr);
|
||||
System.out.println("计算结果: " + list);
|
||||
List<String> list1 = new ArrayList<>();
|
||||
for (String s : list) {
|
||||
String o = replaceMap.get(s);
|
||||
if (StringUtils.isNotEmpty(o)) {
|
||||
list1.add(o);
|
||||
} else {
|
||||
list1.add(s);
|
||||
}
|
||||
}
|
||||
return caculateAnalyse(list1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 公式计算 后序list
|
||||
*
|
||||
* @param suffixList
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal caculateAnalyse(List<String> suffixList) {
|
||||
|
||||
BigDecimal a = BigDecimal.ZERO;
|
||||
BigDecimal b = BigDecimal.ZERO;
|
||||
// 构建一个操作数栈 每当获得操作符号时取出最上面两个数进行计算。
|
||||
Stack<BigDecimal> caculateStack = new Stack<BigDecimal>();
|
||||
if (suffixList.size() > 1) {
|
||||
|
||||
for (int i = 0; i < suffixList.size(); i++) {
|
||||
String temp = suffixList.get(i);
|
||||
if (symbol.contains(temp)) {
|
||||
b = caculateStack.pop();
|
||||
a = caculateStack.pop();
|
||||
a = caculate(a, b, temp.toCharArray()[0]);
|
||||
caculateStack.push(a);
|
||||
} else {
|
||||
if (isNumber(suffixList.get(i))) {
|
||||
caculateStack.push(new BigDecimal(suffixList.get(i)));
|
||||
} else {
|
||||
throw new RuntimeException("公式异常!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (suffixList.size() == 1) {
|
||||
String temp = suffixList.get(0);
|
||||
if (isNumber(temp)) {
|
||||
a = BigDecimal.valueOf(Double.parseDouble(temp));
|
||||
} else {
|
||||
throw new RuntimeException("公式异常!");
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算 使用double 进行计算 如果需要可以在这里使用bigdecimal 进行计算
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param symbol
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal caculate(BigDecimal a, BigDecimal b, char symbol) {
|
||||
switch (symbol) {
|
||||
case '+': {
|
||||
return a.add(b).stripTrailingZeros();
|
||||
}
|
||||
case '-':
|
||||
return a.subtract(b).stripTrailingZeros();
|
||||
case '*':
|
||||
return a.multiply(b);
|
||||
case '/':
|
||||
case '%':
|
||||
int length1 = getDivideLength(a, b);
|
||||
return a.divide(b, length1, BigDecimal.ROUND_HALF_UP);
|
||||
default:
|
||||
throw new RuntimeException("操作符号异常!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int getDivideLength(BigDecimal a, BigDecimal b) {
|
||||
String s1 = a.toString();
|
||||
String s2 = b.toString();
|
||||
int length1 = 0;
|
||||
int length2 = 0;
|
||||
if (s1.contains(".")) {
|
||||
length1 = s1.split("\\.")[1].length();
|
||||
}
|
||||
if (s2.contains(".")) {
|
||||
length2 = s2.split("\\.")[1].length();
|
||||
}
|
||||
if (length1 == 0 && length2 == 0) {
|
||||
return 2;
|
||||
} else {
|
||||
return Math.max(length1, length2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串直接 转 后序
|
||||
*/
|
||||
public static List<String> suffixHandle(String exeStr) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Stack stack = new Stack();
|
||||
char[] exeChars = exeStr.toCharArray();
|
||||
List<String> res = new ArrayList<>();
|
||||
for (char x : exeChars) {
|
||||
// 判断是不是操作符号
|
||||
if (symbol.indexOf(x) > -1) {
|
||||
// 不管怎样先将数据添加进列表
|
||||
if (buf.length() > 0) {
|
||||
// 添加数据到res
|
||||
String temp = buf.toString();
|
||||
// 验证是否为变量或数字
|
||||
if (!isVariableAndNumber(temp)) {
|
||||
throw new RuntimeException(buf.append(" 格式不对").toString());
|
||||
}
|
||||
|
||||
// 添加到结果列表中
|
||||
res.add(temp);
|
||||
// 清空临时buf
|
||||
buf.delete(0, buf.length());
|
||||
}
|
||||
if (!stack.isEmpty()) {
|
||||
|
||||
//2.判断是不是开是括号
|
||||
if (x == '(') {
|
||||
stack.push(x);
|
||||
continue;
|
||||
}
|
||||
//3.判断是不是闭合括号
|
||||
if (x == ')') {
|
||||
boolean a = false;
|
||||
while (!stack.isEmpty()) {
|
||||
char con = (char) stack.peek();
|
||||
if (con == '(' && !a) {
|
||||
stack.pop();
|
||||
a = true;
|
||||
} else if (!a) {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 遵循四则运算法则
|
||||
int size = stack.size();
|
||||
while (size > 0) {
|
||||
char con = (char) stack.peek();
|
||||
if (compare(con, x) > 0) {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
}
|
||||
size--;
|
||||
}
|
||||
stack.push(x);
|
||||
|
||||
} else {
|
||||
stack.push(x);
|
||||
}
|
||||
} else {
|
||||
buf.append(x);
|
||||
}
|
||||
}
|
||||
if (buf.length() > 0) {
|
||||
res.add(buf.toString());
|
||||
}
|
||||
while (!stack.isEmpty()) {
|
||||
res.add(String.valueOf(stack.pop()));
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个操作符号的优先级
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public static int compare(char a, char b) {
|
||||
String s1 = String.valueOf(a);
|
||||
String s2 = String.valueOf(b);
|
||||
Integer ai = symbol_map.get(s1);
|
||||
Integer bi = symbol_map.get(s2);
|
||||
if (null != ai && null != bi) {
|
||||
if (ai <= bi) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否为数 字符串
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isNumber(String str) {
|
||||
Pattern pattern = Pattern.compile("[-+]?\\d+(?:\\.\\d+)?");
|
||||
Matcher isNum = pattern.matcher(str);
|
||||
return isNum.matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为数 字符串
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isVariable(String str) {
|
||||
Pattern pattern = Pattern.compile("^[A-Z]+$");
|
||||
Matcher isNum = pattern.matcher(str);
|
||||
return isNum.matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为数 字符串
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isVariableAndNumber(String str) {
|
||||
Pattern pattern = Pattern.compile("[A-Z]|-?\\d+(\\.\\d+)?");
|
||||
Matcher isNum = pattern.matcher(str);
|
||||
return isNum.matches();
|
||||
}
|
||||
|
||||
public static String caculateReplace(String str, Map<String, String> map) {
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
str = str.replaceAll(entry.getKey(), entry.getValue()==null ? "1" : entry.getValue());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String toFloat(byte[] bytes) throws IOException {
|
||||
ByteArrayInputStream mByteArrayInputStream = new ByteArrayInputStream(bytes);
|
||||
DataInputStream mDataInputStream = new DataInputStream(mByteArrayInputStream);
|
||||
try {
|
||||
float v = mDataInputStream.readFloat();
|
||||
return String.format("%.6f",v);
|
||||
}catch (Exception e){
|
||||
throw new ServiceException("modbus16转浮点数错误");
|
||||
}
|
||||
finally {
|
||||
mDataInputStream.close();
|
||||
mByteArrayInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转16位无符号整形
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign16(long value) {
|
||||
long unSigned = value & 0xFFFF;
|
||||
return unSigned +""; // 将字节数组转换为十六进制字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位有符号CDAB数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toSign32_CDAB(long value) {
|
||||
byte[] bytes = intToBytes2((int) value);
|
||||
return bytesToInt2(bytes)+"";
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位无符号ABCD数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign32_ABCD(long value) {
|
||||
return Integer.toUnsignedString((int) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位无符号CDAB数据类型
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String toUnSign32_CDAB(long value) {
|
||||
byte[] bytes = intToBytes2((int) value);
|
||||
int val = bytesToInt2(bytes);
|
||||
return Integer.toUnsignedString(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转32位浮点数 ABCD
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static float toFloat32_ABCD(byte[] bytes) {
|
||||
int intValue = (bytes[0] << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
|
||||
return Float.intBitsToFloat(intValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转32位浮点数 CDAB
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static Float toFloat32_CDAB(byte[] bytes) {
|
||||
int intValue = ((bytes[2] & 0xFF) << 24) | ((bytes[3] & 0xFF) << 16) | ((bytes[0] & 0xFF) << 8) | ((bytes[1] & 0xFF)) ;
|
||||
return Float.intBitsToFloat(intValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用
|
||||
*/
|
||||
public static int bytesToInt2(byte[] src) {
|
||||
return (((src[2] & 0xFF) << 24) | ((src[3] & 0xFF) << 16) | ((src[0] & 0xFF) << 8) | (src[1] & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用
|
||||
*/
|
||||
public static byte[] intToBytes2(int value) {
|
||||
byte[] src = new byte[4];
|
||||
src[0] = (byte) ((value >> 24) & 0xFF);
|
||||
src[1] = (byte) ((value >> 16) & 0xFF);
|
||||
src[2] = (byte) ((value >> 8) & 0xFF);
|
||||
src[3] = (byte) (value & 0xFF);
|
||||
return src;
|
||||
}
|
||||
|
||||
public static String subHexValue(String hexString){
|
||||
//截取报文中的值
|
||||
String substring = hexString.substring(4, 6);
|
||||
int index = Integer.parseInt(substring);
|
||||
return hexString.substring(6, 6 + index*2);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
String s1 = "A/B*C"; // 1.5
|
||||
String s2 = "E-((A+B)-(C+D))%10"; // 10.4
|
||||
String s3 = "A-B-C*(D-E)+10*5"; // 67
|
||||
String s4 = "A-B-C*(D+E)-(A+B)+(2+3)"; // -41
|
||||
String s5 = "A-(A-(B-C)*(D+E))%10+B"; // 1.5
|
||||
String s6 = "A-(B+C)*D+10"; // -9
|
||||
String s7 = "1+2*3-2+2*(1-2+3*4+5-6/2+(2-1)+3*4-2)%10"; // 9.8
|
||||
|
||||
|
||||
boolean number = isNumber("-10");
|
||||
System.out.println(number);
|
||||
|
||||
Map<String, String> replaceMap = new HashMap<>();
|
||||
replaceMap.put("A", "1");
|
||||
replaceMap.put("B", "2");
|
||||
replaceMap.put("C", "3");
|
||||
replaceMap.put("D", "4");
|
||||
replaceMap.put("E", "10");
|
||||
BigDecimal execute = execute(s7, replaceMap);
|
||||
System.out.println(execute);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
{
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static String SS_MM_HH_DD_HH_YY = "ssmmHHddMMyy";
|
||||
|
||||
public static String YY_MM_DD_HH_MM_SS = "yy-MM-dd HH:mm:ss";
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate()
|
||||
{
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate()
|
||||
{
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static final String getTime()
|
||||
{
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow()
|
||||
{
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow(final String format)
|
||||
{
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static final String dateTime(final Date date)
|
||||
{
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String parseDateToStr(final String format, final Date date)
|
||||
{
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static final Date dateTime(final String format, final String ts)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static final String datePath()
|
||||
{
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static final String dateTime()
|
||||
{
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static final String dateTimeYY(Date date)
|
||||
{
|
||||
return DateFormatUtils.format(date, YY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器启动时间
|
||||
*/
|
||||
public static Date getServerStartDate()
|
||||
{
|
||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
return new Date(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差天数
|
||||
*/
|
||||
public static int differentDaysByMillisecond(Date date1, Date date2)
|
||||
{
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差秒数
|
||||
*/
|
||||
public static int differentSeconds(Date date1, Date date2)
|
||||
{
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间差
|
||||
*/
|
||||
public static String getDatePoor(Date endDate, Date nowDate)
|
||||
{
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - nowDate.getTime();
|
||||
// 计算差多少天
|
||||
long day = diff / nd;
|
||||
// 计算差多少小时
|
||||
long hour = diff % nd / nh;
|
||||
// 计算差多少分钟
|
||||
long min = diff % nd % nh / nm;
|
||||
// 计算差多少秒//输出结果
|
||||
// long sec = diff % nd % nh % nm / ns;
|
||||
return day + "天" + hour + "小时" + min + "分钟";
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDateTime ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDateTime temporalAccessor)
|
||||
{
|
||||
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDate ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDate temporalAccessor)
|
||||
{
|
||||
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
||||
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
|
||||
public static long getTimestamp(){
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static long getTimestampSeconds(){
|
||||
return System.currentTimeMillis()/1000;
|
||||
}
|
||||
|
||||
public static String generateRandomHex(int length) {
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
// 添加"D"作为开头
|
||||
sb.append("D");
|
||||
for (int i = 1; i < length; i++) {
|
||||
int randomInt = random.nextInt(16); // 生成0到15的随机整数
|
||||
char hexChar = Character.toUpperCase(Character.forDigit(randomInt, 16)); // 将整数转换为十六进制字符并转为大写
|
||||
sb.append(hexChar);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Date date = DateUtils.dateTime(SS_MM_HH_DD_HH_YY, "434123181121");
|
||||
String s = DateUtils.dateTimeYY(date);
|
||||
System.out.println(s);
|
||||
|
||||
String s1 = generateRandomHex(12);
|
||||
System.out.println(s1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串去除毫秒
|
||||
* @param time 时间字符串
|
||||
* @return java.lang.String
|
||||
*/
|
||||
public static String strRemoveMs(String time) {
|
||||
Date date = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS_SSS, time);
|
||||
return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期去除毫秒
|
||||
* @param time 时间
|
||||
* @return java.util.Date
|
||||
*/
|
||||
public static Date dateRemoveMs(Date time) {
|
||||
String s = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, time);
|
||||
return DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.fastbee.common.constant.CacheConstants;
|
||||
import com.fastbee.common.core.domain.entity.SysDictData;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
|
||||
import static com.fastbee.common.constant.Constants.EN_US;
|
||||
import static com.fastbee.common.constant.Constants.ZH_CN;
|
||||
|
||||
/**
|
||||
* 字典工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DictUtils
|
||||
{
|
||||
/**
|
||||
* 分隔符
|
||||
*/
|
||||
public static final String SEPARATOR = ",";
|
||||
|
||||
/**
|
||||
* 设置字典缓存
|
||||
*
|
||||
* @param key 参数键
|
||||
* @param dictDatas 字典数据列表
|
||||
*/
|
||||
public static void setDictCache(String key, List<SysDictData> dictDatas)
|
||||
{
|
||||
SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典缓存
|
||||
*
|
||||
* @param key 参数键
|
||||
* @return dictDatas 字典数据列表
|
||||
*/
|
||||
public static List<SysDictData> getDictCache(String key)
|
||||
{
|
||||
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNotNull(arrayCache))
|
||||
{
|
||||
return arrayCache.toList(SysDictData.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @param language 语言
|
||||
* @return 字典标签
|
||||
*/
|
||||
public static String getDictLabel(String dictType, String dictValue, String language)
|
||||
{
|
||||
return getDictLabel(dictType, dictValue, SEPARATOR, language);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @return 字典值
|
||||
*/
|
||||
public static String getDictValue(String dictType, String dictLabel)
|
||||
{
|
||||
return getDictValue(dictType, dictLabel, SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @param separator 分隔符
|
||||
* @param language 语言
|
||||
* @return 字典标签
|
||||
*/
|
||||
public static String getDictLabel(String dictType, String dictValue, String separator, String language)
|
||||
{
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StringUtils.isNotNull(datas))
|
||||
{
|
||||
if (StringUtils.containsAny(separator, dictValue))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
for (String value : dictValue.split(separator))
|
||||
{
|
||||
if (value.equals(dict.getDictValue()))
|
||||
{
|
||||
propertyString.append(dict.getDictLabel()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
if (dictValue.equals(dict.getDictValue()))
|
||||
{
|
||||
if (ZH_CN.equals(language)) {
|
||||
return dict.getDictLabel_zh_CN();
|
||||
} else if (EN_US.equals(language)) {
|
||||
return dict.getDictLabel_en_US();
|
||||
} else {
|
||||
return dict.getDictLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @param separator 分隔符
|
||||
* @return 字典值
|
||||
*/
|
||||
public static String getDictValue(String dictType, String dictLabel, String separator)
|
||||
{
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = getDictCache(dictType);
|
||||
|
||||
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas))
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
for (String label : dictLabel.split(separator))
|
||||
{
|
||||
if (label.equals(dict.getDictLabel()))
|
||||
{
|
||||
propertyString.append(dict.getDictValue()).append(separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (SysDictData dict : datas)
|
||||
{
|
||||
if (dictLabel.equals(dict.getDictLabel()))
|
||||
{
|
||||
return dict.getDictValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定字典缓存
|
||||
*
|
||||
* @param key 字典键
|
||||
*/
|
||||
public static void removeDictCache(String key)
|
||||
{
|
||||
SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空字典缓存
|
||||
*/
|
||||
public static void clearDictCache()
|
||||
{
|
||||
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*");
|
||||
SpringUtils.getBean(RedisCache.class).deleteObject(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cache key
|
||||
*
|
||||
* @param configKey 参数键
|
||||
* @return 缓存键key
|
||||
*/
|
||||
public static String getCacheKey(String configKey)
|
||||
{
|
||||
return CacheConstants.SYS_DICT_KEY + configKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.fastbee.common.utils.uuid.IdUtils;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class DigestUtils {
|
||||
private static SecureRandom random = new SecureRandom();
|
||||
private static IdUtils idUtils = new IdUtils(0,0);
|
||||
|
||||
public static String getId(){
|
||||
return String.valueOf(Math.abs(random.nextLong()));
|
||||
}
|
||||
|
||||
public static String nextId(){
|
||||
return String.valueOf(idUtils.nextId());
|
||||
}
|
||||
|
||||
|
||||
public static byte[] genSalt(int numBytes) {
|
||||
Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", (long)numBytes);
|
||||
byte[] bytes = new byte[numBytes];
|
||||
random.nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance(algorithm);
|
||||
if(salt != null) {
|
||||
digest.update(salt);
|
||||
}
|
||||
|
||||
byte[] result = digest.digest(input);
|
||||
|
||||
for(int i = 1; i < iterations; ++i) {
|
||||
digest.reset();
|
||||
result = digest.digest(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (GeneralSecurityException var7) {
|
||||
throw ExceptionUtils.unchecked(var7);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] digest(InputStream input, String algorithm) throws IOException {
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
|
||||
int bufferLength = 8192;
|
||||
byte[] buffer = new byte[bufferLength];
|
||||
|
||||
for(int read = input.read(buffer, 0, bufferLength); read > -1; read = input.read(buffer, 0, bufferLength)) {
|
||||
messageDigest.update(buffer, 0, read);
|
||||
}
|
||||
|
||||
return messageDigest.digest();
|
||||
} catch (GeneralSecurityException var6) {
|
||||
throw ExceptionUtils.unchecked(var6);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.println(nextId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
public class EmqxUtils {
|
||||
|
||||
//获取
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EncodeUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EncodeUtils.class);
|
||||
private static final String DEFAULT_URL_ENCODING = "UTF-8";
|
||||
private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
|
||||
private static Pattern p1 = Pattern.compile("<\\s*(script|link|style|iframe)([\\s\\S]+?)<\\/\\s*\\1\\s*>", 2);
|
||||
private static Pattern p2 = Pattern.compile("\\s*on[a-z]+\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s]+)\\s*(?=>)", 2);
|
||||
private static Pattern p3 = Pattern.compile("\\s*(href|src)\\s*=\\s*(\"\\s*(javascript|vbscript):[^\"]+\"|'\\s*(javascript|vbscript):[^']+'|(javascript|vbscript):[^\\s]+)\\s*(?=>)", 2);
|
||||
private static Pattern p4 = Pattern.compile("epression\\((.|\\n)*\\);?", 2);
|
||||
private static Pattern p5 = Pattern.compile("(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)", 2);
|
||||
|
||||
public EncodeUtils() {
|
||||
}
|
||||
|
||||
public static String encodeHex(byte[] input) {
|
||||
return new String(Hex.encode(input));
|
||||
}
|
||||
|
||||
public static byte[] decodeHex(String input) {
|
||||
try {
|
||||
return Hex.decode(input);
|
||||
} catch (Exception var2) {
|
||||
throw ExceptionUtils.unchecked(var2);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeBase64(byte[] input) {
|
||||
return new String(Base64.encodeBase64(input));
|
||||
}
|
||||
|
||||
public static String encodeBase64(String input) {
|
||||
try {
|
||||
return new String(Base64.encodeBase64(input.getBytes("UTF-8")));
|
||||
} catch (UnsupportedEncodingException var2) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String input) {
|
||||
return Base64.decodeBase64(input.getBytes());
|
||||
}
|
||||
|
||||
public static String decodeBase64String(String input) {
|
||||
try {
|
||||
return new String(Base64.decodeBase64(input.getBytes()), "UTF-8");
|
||||
} catch (UnsupportedEncodingException var2) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeBase62(byte[] input) {
|
||||
char[] chars = new char[input.length];
|
||||
|
||||
for(int i = 0; i < input.length; ++i) {
|
||||
chars[i] = BASE62[(input[i] & 255) % BASE62.length];
|
||||
}
|
||||
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public static String encodeHtml(String html) {
|
||||
return StringEscapeUtils.escapeHtml4(html);
|
||||
}
|
||||
|
||||
public static String decodeHtml(String htmlEscaped) {
|
||||
return StringEscapeUtils.unescapeHtml4(htmlEscaped);
|
||||
}
|
||||
|
||||
public static String encodeXml(String xml) {
|
||||
return StringEscapeUtils.escapeXml(xml);
|
||||
}
|
||||
|
||||
public static String decodeXml(String xmlEscaped) {
|
||||
return StringEscapeUtils.unescapeXml(xmlEscaped);
|
||||
}
|
||||
|
||||
public static String encodeUrl(String part) {
|
||||
return encodeUrl(part, "UTF-8");
|
||||
}
|
||||
|
||||
public static String encodeUrl(String part, String encoding) {
|
||||
if(part == null) {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return URLEncoder.encode(part, encoding);
|
||||
} catch (UnsupportedEncodingException var3) {
|
||||
throw ExceptionUtils.unchecked(var3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String decodeUrl(String part) {
|
||||
return decodeUrl(part, "UTF-8");
|
||||
}
|
||||
|
||||
public static String decodeUrl(String part, String encoding) {
|
||||
try {
|
||||
return URLDecoder.decode(part, encoding);
|
||||
} catch (UnsupportedEncodingException var3) {
|
||||
throw ExceptionUtils.unchecked(var3);
|
||||
}
|
||||
}
|
||||
|
||||
public static String decodeUrl2(String part) {
|
||||
return decodeUrl(decodeUrl(part));
|
||||
}
|
||||
|
||||
public static String xssFilter(String text) {
|
||||
if(text == null) {
|
||||
return null;
|
||||
} else {
|
||||
String oriValue = StringUtils.trim(text);
|
||||
String value = p1.matcher(oriValue).replaceAll("");
|
||||
value = p2.matcher(value).replaceAll("");
|
||||
value = p3.matcher(value).replaceAll("");
|
||||
value = p4.matcher(value).replaceAll("");
|
||||
if(!StringUtils.startsWithIgnoreCase(value, "<!--HTML-->") && !StringUtils.startsWithIgnoreCase(value, "<?xml ") && !StringUtils.contains(value, "id=\"FormHtml\"") && (!StringUtils.startsWith(value, "{") || !StringUtils.endsWith(value, "}")) && (!StringUtils.startsWith(value, "[") || !StringUtils.endsWith(value, "]"))) {
|
||||
value = value.replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
||||
}
|
||||
|
||||
if(logger.isInfoEnabled() && !value.equals(oriValue)) {
|
||||
logger.info("xssFilter: {} to {}", text, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static String sqlFilter(String text) {
|
||||
if(text != null) {
|
||||
String value = p5.matcher(text).replaceAll("");
|
||||
if(logger.isWarnEnabled() && !value.equals(text)) {
|
||||
logger.warn("sqlFilter: {} to {}", text, value);
|
||||
return "";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static MultipartFile base64toMultipartFile(String base64) {
|
||||
final String[] base64Array = base64.split(",");
|
||||
String dataUir, data;
|
||||
if (base64Array.length > 1) {
|
||||
dataUir = base64Array[0];
|
||||
data = base64Array[1];
|
||||
} else {
|
||||
//根据你base64代表的具体文件构建
|
||||
dataUir = "data:image/png;base64";
|
||||
data = base64Array[0];
|
||||
}
|
||||
return new Base64ToMultipartFile(data, dataUir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
/**
|
||||
* 错误信息处理类。
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ExceptionUtil
|
||||
{
|
||||
/**
|
||||
* 获取exception的详细错误信息。
|
||||
*/
|
||||
public static String getExceptionMessage(Throwable e)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw, true));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static String getRootErrorMessage(Exception e)
|
||||
{
|
||||
Throwable root = ExceptionUtils.getRootCause(e);
|
||||
root = (root == null ? e : root);
|
||||
if (root == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
String msg = root.getMessage();
|
||||
if (msg == null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
return StringUtils.defaultString(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class ExceptionUtils {
|
||||
|
||||
|
||||
public static Throwable getThrowable(HttpServletRequest request) {
|
||||
Throwable ex = null;
|
||||
if(request.getAttribute("exception") != null) {
|
||||
ex = (Throwable)request.getAttribute("exception");
|
||||
} else if(request.getAttribute("javax.servlet.error.exception") != null) {
|
||||
ex = (Throwable)request.getAttribute("javax.servlet.error.exception");
|
||||
}
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
public static String getStackTraceAsString(Throwable e) {
|
||||
if(e == null) {
|
||||
return "";
|
||||
} else {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(stringWriter));
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isCausedBy(Exception ex, Class... causeExceptionClasses) {
|
||||
for(Throwable cause = ex.getCause(); cause != null; cause = cause.getCause()) {
|
||||
Class[] var3 = causeExceptionClasses;
|
||||
int var4 = causeExceptionClasses.length;
|
||||
|
||||
for(int var5 = 0; var5 < var4; ++var5) {
|
||||
Class<? extends Exception> causeClass = var3[var5];
|
||||
if(causeClass.isInstance(cause)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static RuntimeException unchecked(Exception e) {
|
||||
return e instanceof RuntimeException?(RuntimeException)e:new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
/**
|
||||
* 处理并记录日志文件
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LogUtils
|
||||
{
|
||||
public static String getBlock(Object msg)
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
msg = "";
|
||||
}
|
||||
return "[" + msg.toString() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.fastbee.common.core.text.KeyValue;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Map 工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class MapUtils {
|
||||
|
||||
/**
|
||||
* 从哈希表表中,获得 keys 对应的所有 value 数组
|
||||
*
|
||||
* @param multimap 哈希表
|
||||
* @param keys keys
|
||||
* @return value 数组
|
||||
*/
|
||||
public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
|
||||
List<V> result = new ArrayList<>();
|
||||
keys.forEach(k -> {
|
||||
Collection<V> values = multimap.get(k);
|
||||
if (CollectionUtil.isEmpty(values)) {
|
||||
return;
|
||||
}
|
||||
result.addAll(values);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从哈希表查找到 key 对应的 value,然后进一步处理
|
||||
* 注意,如果查找到的 value 为 null 时,不进行处理
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param key key
|
||||
* @param consumer 进一步处理的逻辑
|
||||
*/
|
||||
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
|
||||
if (CollUtil.isEmpty(map)) {
|
||||
return;
|
||||
}
|
||||
V value = map.get(key);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
consumer.accept(value);
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
|
||||
Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
|
||||
keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class Md5Utils {
|
||||
private static final String MD5 = "MD5";
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
|
||||
|
||||
public static String md5(String input) {
|
||||
return md5((String) input, 1);
|
||||
}
|
||||
|
||||
public static String md5(String input, int iterations) {
|
||||
try {
|
||||
return EncodeUtils.encodeHex(DigestUtils.digest(input.getBytes("UTF-8"), "MD5", (byte[]) null, iterations));
|
||||
} catch (UnsupportedEncodingException var3) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] md5(byte[] input) {
|
||||
return md5((byte[]) input, 1);
|
||||
}
|
||||
|
||||
public static byte[] md5(byte[] input, int iterations) {
|
||||
return DigestUtils.digest(input, "MD5", (byte[]) null, iterations);
|
||||
}
|
||||
|
||||
public static byte[] md5(InputStream input) throws IOException {
|
||||
return DigestUtils.digest(input, "MD5");
|
||||
}
|
||||
|
||||
public static boolean isMd5(String str) {
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
switch (str.charAt(i)) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
++cnt;
|
||||
if (32 <= cnt) return true;
|
||||
break;
|
||||
case '/':
|
||||
if ((i + 10) < str.length()) {// "/storage/"
|
||||
char ch1 = str.charAt(i + 1);
|
||||
char ch2 = str.charAt(i + 8);
|
||||
if ('/' == ch2 && ('s' == ch1 || 'S' == ch1)) return true;
|
||||
}
|
||||
default:
|
||||
cnt = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
|
||||
/**
|
||||
* 获取i18n资源文件
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class MessageUtils
|
||||
{
|
||||
/**
|
||||
* 根据消息键和参数 获取消息 委托给spring messageSource
|
||||
*
|
||||
* @param code 消息键
|
||||
* @param args 参数
|
||||
* @return 获取国际化翻译值
|
||||
*/
|
||||
public static String message(String code, Object... args)
|
||||
{
|
||||
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
|
||||
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.fastbee.common.core.page.PageDomain;
|
||||
import com.fastbee.common.core.page.TableSupport;
|
||||
import com.fastbee.common.utils.sql.SqlUtil;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PageUtils extends PageHelper
|
||||
{
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
public static void startPage()
|
||||
{
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Integer pageNum = pageDomain.getPageNum();
|
||||
Integer pageSize = pageDomain.getPageSize();
|
||||
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
|
||||
Boolean reasonable = pageDomain.getReasonable();
|
||||
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理分页的线程变量
|
||||
*/
|
||||
public static void clearPage()
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import com.fastbee.common.constant.HttpStatus;
|
||||
import com.fastbee.common.core.domain.model.LoginUser;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
|
||||
/**
|
||||
* 安全服务工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SecurityUtils
|
||||
{
|
||||
/**
|
||||
* 用户ID
|
||||
**/
|
||||
public static Long getUserId()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getLoginUser().getUserId();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
**/
|
||||
public static Long getDeptId()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getLoginUser().getDeptId();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
**/
|
||||
public static String getUsername()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getLoginUser().getUsername();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
**/
|
||||
public static LoginUser getLoginUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (LoginUser) getAuthentication().getPrincipal();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Authentication
|
||||
*/
|
||||
public static Authentication getAuthentication()
|
||||
{
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成BCryptPasswordEncoder密码
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密字符串
|
||||
*/
|
||||
public static String encryptPassword(String password)
|
||||
{
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断密码是否相同
|
||||
*
|
||||
* @param rawPassword 真实密码
|
||||
* @param encodedPassword 加密后字符
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean matchesPassword(String rawPassword, String encodedPassword)
|
||||
{
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言
|
||||
* @return
|
||||
*/
|
||||
public static String getLanguage(){
|
||||
try
|
||||
{
|
||||
String language = getLoginUser().getLanguage();
|
||||
if (StringUtils.isEmpty(language)){
|
||||
return "en-US";
|
||||
}
|
||||
return language;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return "en-US";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.core.text.Convert;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ServletUtils
|
||||
{
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name)
|
||||
{
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue)
|
||||
{
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name)
|
||||
{
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name, Boolean defaultValue)
|
||||
{
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String[]> getParams(ServletRequest request)
|
||||
{
|
||||
final Map<String, String[]> map = request.getParameterMap();
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String> getParamMap(ServletRequest request)
|
||||
{
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet())
|
||||
{
|
||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest()
|
||||
{
|
||||
return getRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse()
|
||||
{
|
||||
return getRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession()
|
||||
{
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes()
|
||||
{
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
*/
|
||||
public static void renderString(HttpServletResponse response, String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setStatus(200);
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static boolean isAjaxRequest(HttpServletRequest request)
|
||||
{
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.contains("application/json"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String ajax = request.getParameter("__ajax");
|
||||
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容编码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
public static String urlEncode(String str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return URLEncoder.encode(str, Constants.UTF8);
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容解码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 解码后的内容
|
||||
*/
|
||||
public static String urlDecode(String str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return URLDecoder.decode(str, Constants.UTF8);
|
||||
}
|
||||
catch (UnsupportedEncodingException e)
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getClientIP() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
return ServletUtil.getClientIP(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,832 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.core.text.StrFormatter;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/**
|
||||
* 下划线
|
||||
*/
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param value defaultValue 要判断的value
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static <T> T nvl(T value, T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> coll) {
|
||||
return isNull(coll) || coll.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> coll) {
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否为空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* * @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Object[] objects) {
|
||||
return isNull(objects) || (objects.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否非空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Object[] objects) {
|
||||
return !isEmpty(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return isNull(map) || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return isNull(str) || NULLSTR.equals(str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty(String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否为空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isNull(Object object) {
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否非空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotNull(Object object) {
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||
*
|
||||
* @param object 对象
|
||||
* @return true:是数组 false:不是数组
|
||||
*/
|
||||
public static boolean isArray(Object object) {
|
||||
return isNotNull(object) && object.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start, int end) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
end = str.length() + end;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (end > str.length()) {
|
||||
end = str.length();
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
public static String format(String template, Object... params) {
|
||||
if (isEmpty(params) || isEmpty(template)) {
|
||||
return template;
|
||||
}
|
||||
return StrFormatter.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean ishttp(String link) {
|
||||
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转set
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @return set集合
|
||||
*/
|
||||
public static final Set<String> str2Set(String str, String sep) {
|
||||
return new HashSet<String>(str2List(str, sep, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转list
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @param filterBlank 过滤纯空白
|
||||
* @param trim 去掉首尾空白
|
||||
* @return list集合
|
||||
*/
|
||||
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 过滤空白字符串
|
||||
if (filterBlank && StringUtils.isBlank(str)) {
|
||||
return list;
|
||||
}
|
||||
String[] split = str.split(sep);
|
||||
for (String string : split) {
|
||||
if (filterBlank && StringUtils.isBlank(string)) {
|
||||
continue;
|
||||
}
|
||||
if (trim) {
|
||||
string = string.trim();
|
||||
}
|
||||
list.add(string);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
|
||||
*
|
||||
* @param set 给定的集合
|
||||
* @param array 给定的数组
|
||||
* @return boolean 结果
|
||||
*/
|
||||
public static boolean containsAny(Collection<String> collection, String... array) {
|
||||
if (isEmpty(collection) || isEmpty(array)) {
|
||||
return false;
|
||||
} else {
|
||||
for (String str : array) {
|
||||
if (collection.contains(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
|
||||
*
|
||||
* @param cs 指定字符串
|
||||
* @param searchCharSequences 需要检查的字符串数组
|
||||
* @return 是否包含任意一个字符串
|
||||
*/
|
||||
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
|
||||
if (isEmpty(cs) || isEmpty(searchCharSequences)) {
|
||||
return false;
|
||||
}
|
||||
for (CharSequence testStr : searchCharSequences) {
|
||||
if (containsIgnoreCase(cs, testStr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 前置字符是否大写
|
||||
boolean preCharIsUpperCase = true;
|
||||
// 当前字符是否大写
|
||||
boolean curreCharIsUpperCase = true;
|
||||
// 下一字符是否大写
|
||||
boolean nexteCharIsUpperCase = true;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (i > 0) {
|
||||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||
} else {
|
||||
preCharIsUpperCase = false;
|
||||
}
|
||||
|
||||
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||
|
||||
if (i < (str.length() - 1)) {
|
||||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||
}
|
||||
|
||||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase(String str, String... strs) {
|
||||
if (str != null && strs != null) {
|
||||
for (String s : strs) {
|
||||
if (str.equalsIgnoreCase(trim(s))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty()) {
|
||||
// 没必要转换
|
||||
return "";
|
||||
} else if (!name.contains("_")) {
|
||||
// 不含下划线,仅将首字母大写
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String[] camels = name.split("_");
|
||||
for (String camel : camels) {
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
s = s.toLowerCase();
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
boolean upperCase = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == SEPARATOR) {
|
||||
upperCase = true;
|
||||
} else if (upperCase) {
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperCase = false;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches(String str, List<String> strs) {
|
||||
if (isEmpty(str) || isEmpty(strs)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs) {
|
||||
if (isMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast(Object obj) {
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static final String padl(final Number num, final int size) {
|
||||
return padl(num.toString(), size, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||
*/
|
||||
public static final String padl(final String s, final int size, final char c) {
|
||||
final StringBuilder sb = new StringBuilder(size);
|
||||
if (s != null) {
|
||||
final int len = s.length();
|
||||
if (s.length() <= size) {
|
||||
for (int i = size - len; i > 0; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append(s);
|
||||
} else {
|
||||
return s.substring(len - size, len);
|
||||
}
|
||||
} else {
|
||||
for (int i = size; i > 0; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*将字符串转小写,首字母大写,其他小写*/
|
||||
public static String upperCase(String str) {
|
||||
char[] ch = str.toLowerCase().toCharArray();
|
||||
if (ch[0] >= 'a' && ch[0] <= 'z') {
|
||||
ch[0] = (char) (ch[0] - 32);
|
||||
}
|
||||
return new String(ch);
|
||||
}
|
||||
|
||||
public static String toString(Object value) {
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
if (value instanceof ByteBuf) {
|
||||
return ByteBufUtil.hexDump((ByteBuf) value);
|
||||
}
|
||||
if (!value.getClass().isArray()) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
StringBuilder root = new StringBuilder(32);
|
||||
toString(value, root);
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
public static StringBuilder toString(Object value, StringBuilder builder) {
|
||||
if (value == null) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
builder.append('[');
|
||||
int start = builder.length();
|
||||
|
||||
if (value instanceof long[]) {
|
||||
long[] array = (long[]) value;
|
||||
for (long t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof int[]) {
|
||||
int[] array = (int[]) value;
|
||||
for (int t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof short[]) {
|
||||
short[] array = (short[]) value;
|
||||
for (short t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof byte[]) {
|
||||
byte[] array = (byte[]) value;
|
||||
for (byte t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof char[]) {
|
||||
char[] array = (char[]) value;
|
||||
for (char t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof double[]) {
|
||||
double[] array = (double[]) value;
|
||||
for (double t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof float[]) {
|
||||
float[] array = (float[]) value;
|
||||
for (float t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof boolean[]) {
|
||||
boolean[] array = (boolean[]) value;
|
||||
for (boolean t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof String[]) {
|
||||
String[] array = (String[]) value;
|
||||
for (String t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
|
||||
} else if (isArray1(value)) {
|
||||
Object[] array = (Object[]) value;
|
||||
for (Object t : array) {
|
||||
toString(t, builder).append(',');
|
||||
}
|
||||
|
||||
} else if (value instanceof Object[]) {
|
||||
Object[] array = (Object[]) value;
|
||||
for (Object t : array) {
|
||||
builder.append(t).append(',');
|
||||
}
|
||||
}
|
||||
|
||||
int end = builder.length();
|
||||
if (end <= start) {
|
||||
builder.append(']');
|
||||
} else {
|
||||
builder.setCharAt(end - 1, ']');
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static boolean isArray1(Object value) {
|
||||
Class<?> componentType = value.getClass().getComponentType();
|
||||
if (componentType == null) {
|
||||
return false;
|
||||
}
|
||||
return componentType.isArray();
|
||||
}
|
||||
|
||||
public static String leftPad(String str, int size, char ch) {
|
||||
int length = str.length();
|
||||
int pads = size - length;
|
||||
if (pads > 0) {
|
||||
char[] result = new char[size];
|
||||
str.getChars(0, length, result, pads);
|
||||
while (pads > 0) {
|
||||
result[--pads] = ch;
|
||||
}
|
||||
return new String(result);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串中的数字
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static Integer matcherNum(String str){
|
||||
Pattern pattern = Pattern.compile("\\d+");
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
while (matcher.find()){
|
||||
return Integer.parseInt(matcher.group());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串中的变量
|
||||
* @param variable 变量标识符合
|
||||
* @param: str 内容
|
||||
* @return java.util.List<java.lang.String>
|
||||
*/
|
||||
public static List<String> getVariables(String variable, String str) {
|
||||
List<String> variables = new ArrayList<>();
|
||||
Pattern pattern = null;
|
||||
switch (variable) {
|
||||
case "${}":
|
||||
pattern = Pattern.compile("\\$\\{([^}]+)}");
|
||||
break;
|
||||
case "{{}}":
|
||||
pattern = Pattern.compile("\\{\\{([^}]+)}}");
|
||||
break;
|
||||
case "{}":
|
||||
pattern = Pattern.compile("\\{([^}]+)}");
|
||||
break;
|
||||
case "#{}":
|
||||
pattern = Pattern.compile("#\\{([^}]+)}");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assert pattern != null;
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
while (matcher.find()) {
|
||||
variables.add(matcher.group(1));
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信小程序变量
|
||||
* @param content 内容
|
||||
* @return java.util.List<java.lang.String>
|
||||
*/
|
||||
public static List<String> getWeChatMiniVariables(String content) {
|
||||
List<String> variables = new ArrayList<>();
|
||||
Pattern pattern = Pattern.compile("\\{\\{([^}]+)}}");
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
while (matcher.find()) {
|
||||
variables.add(matcher.group(1).replace(".DATA", ""));
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串text中由openToken和closeToken组成的占位符依次替换为args数组中的值
|
||||
* @param openToken
|
||||
* @param closeToken
|
||||
* @param text
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static String parse(String openToken, String closeToken, String text, Object... args) {
|
||||
if (args == null || args.length <= 0) {
|
||||
return text;
|
||||
}
|
||||
int argsIndex = 0;
|
||||
|
||||
if (text == null || text.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
char[] src = text.toCharArray();
|
||||
int offset = 0;
|
||||
// search open token
|
||||
int start = text.indexOf(openToken, offset);
|
||||
if (start == -1) {
|
||||
return text;
|
||||
}
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
StringBuilder expression = null;
|
||||
while (start > -1) {
|
||||
if (start > 0 && src[start - 1] == '\\') {
|
||||
// this open token is escaped. remove the backslash and continue.
|
||||
builder.append(src, offset, start - offset - 1).append(openToken);
|
||||
offset = start + openToken.length();
|
||||
} else {
|
||||
// found open token. let's search close token.
|
||||
if (expression == null) {
|
||||
expression = new StringBuilder();
|
||||
} else {
|
||||
expression.setLength(0);
|
||||
}
|
||||
builder.append(src, offset, start - offset);
|
||||
offset = start + openToken.length();
|
||||
int end = text.indexOf(closeToken, offset);
|
||||
while (end > -1) {
|
||||
if (end > offset && src[end - 1] == '\\') {
|
||||
// this close token is escaped. remove the backslash and continue.
|
||||
expression.append(src, offset, end - offset - 1).append(closeToken);
|
||||
offset = end + closeToken.length();
|
||||
end = text.indexOf(closeToken, offset);
|
||||
} else {
|
||||
expression.append(src, offset, end - offset);
|
||||
offset = end + closeToken.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end == -1) {
|
||||
// close token was not found.
|
||||
builder.append(src, start, src.length - start);
|
||||
offset = src.length;
|
||||
} else {
|
||||
///////////////////////////////////////仅仅修改了该else分支下的个别行代码////////////////////////
|
||||
|
||||
String value = (argsIndex <= args.length - 1) ?
|
||||
(args[argsIndex] == null ? "" : args[argsIndex].toString()) : expression.toString();
|
||||
builder.append(value);
|
||||
offset = end + closeToken.length();
|
||||
argsIndex++;
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
start = text.indexOf(openToken, offset);
|
||||
}
|
||||
if (offset < src.length) {
|
||||
builder.append(src, offset, src.length - offset);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
//
|
||||
public static String reverse(String str) {
|
||||
return new StringBuilder(str).reverse().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 替换 ${variable}
|
||||
* @author fastb
|
||||
* @date 2023-12-26 15:35
|
||||
* @version 1.0
|
||||
*/
|
||||
public static String parseVariable(String text, Object... args) {
|
||||
return parse("${", "}", text, args);
|
||||
}
|
||||
|
||||
public static String strReplaceVariable(String openIndex, String closeIndex, String content, LinkedHashMap<String, String> map) {
|
||||
if (StringUtils.isEmpty(content) || MapUtils.isEmpty(map)) {
|
||||
return content;
|
||||
}
|
||||
StringBuilder sendContent = new StringBuilder(content);
|
||||
for (Map.Entry<String, String> m : map.entrySet()) {
|
||||
sendContent = new StringBuilder(sendContent.toString().replace(openIndex + m.getKey() + closeIndex, m.getValue()));
|
||||
}
|
||||
return sendContent.toString();
|
||||
}
|
||||
|
||||
public static List<String> splitEvenly(String str, int size) {
|
||||
List<String> parts = new ArrayList<>();
|
||||
int length = str.length();
|
||||
if (size > length || size <= 0) {
|
||||
throw new IllegalArgumentException("Size is too large or too small.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i += size) {
|
||||
parts.add(str.substring(i, Math.min(length, i + size)));
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 线程相关工具类.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Threads
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
|
||||
|
||||
/**
|
||||
* sleep等待,单位为毫秒
|
||||
*/
|
||||
public static void sleep(long milliseconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(milliseconds);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止线程池
|
||||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
||||
* 如果仍然超時,則強制退出.
|
||||
* 另对在shutdown时线程本身被调用中断做了处理.
|
||||
*/
|
||||
public static void shutdownAndAwaitTermination(ExecutorService pool)
|
||||
{
|
||||
if (pool != null && !pool.isShutdown())
|
||||
{
|
||||
pool.shutdown();
|
||||
try
|
||||
{
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||
{
|
||||
pool.shutdownNow();
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||
{
|
||||
logger.info("Pool did not terminate");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
pool.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印线程异常信息
|
||||
*/
|
||||
public static void printException(Runnable r, Throwable t)
|
||||
{
|
||||
if (t == null && r instanceof Future<?>)
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> future = (Future<?>) r;
|
||||
if (future.isDone())
|
||||
{
|
||||
future.get();
|
||||
}
|
||||
}
|
||||
catch (CancellationException ce)
|
||||
{
|
||||
t = ce;
|
||||
}
|
||||
catch (ExecutionException ee)
|
||||
{
|
||||
t = ee.getCause();
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (t != null)
|
||||
{
|
||||
logger.error(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 校验工具类
|
||||
*
|
||||
* @author fastbee
|
||||
*/
|
||||
public class ValidationUtils {
|
||||
|
||||
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
|
||||
|
||||
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
|
||||
|
||||
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
|
||||
|
||||
private static final Pattern PATTERN_EMAIL = Pattern.compile("^(\\w+([-.][A-Za-z0-9]+)*){3,18}@\\w+([-.][A-Za-z0-9]+)*\\.\\w+([-.][A-Za-z0-9]+)*$");
|
||||
|
||||
public static boolean isMobile(String mobile) {
|
||||
return StringUtils.hasText(mobile)
|
||||
&& PATTERN_MOBILE.matcher(mobile).matches();
|
||||
}
|
||||
|
||||
public static boolean isURL(String url) {
|
||||
return StringUtils.hasText(url)
|
||||
&& PATTERN_URL.matcher(url).matches();
|
||||
}
|
||||
|
||||
public static boolean isXmlNCName(String str) {
|
||||
return StringUtils.hasText(str)
|
||||
&& PATTERN_XML_NCNAME.matcher(str).matches();
|
||||
}
|
||||
|
||||
public static boolean isEmail(String email) {
|
||||
return StringUtils.hasText(email)
|
||||
&& PATTERN_EMAIL.matcher(email).matches();
|
||||
}
|
||||
|
||||
public static void validate(Object object, Class<?>... groups) {
|
||||
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
Assert.notNull(validator);
|
||||
validate(validator, object, groups);
|
||||
}
|
||||
|
||||
public static void validate(Validator validator, Object object, Class<?>... groups) {
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (CollUtil.isNotEmpty(constraintViolations)) {
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package com.fastbee.common.utils;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* 验证码工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class VerifyCodeUtils
|
||||
{
|
||||
// 使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
|
||||
public static final String VERIFY_CODES = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||
|
||||
private static Random random = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 使用系统默认字符源生成验证码
|
||||
*
|
||||
* @param verifySize 验证码长度
|
||||
* @return
|
||||
*/
|
||||
public static String generateVerifyCode(int verifySize)
|
||||
{
|
||||
return generateVerifyCode(verifySize, VERIFY_CODES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定源生成验证码
|
||||
*
|
||||
* @param verifySize 验证码长度
|
||||
* @param sources 验证码字符源
|
||||
* @return
|
||||
*/
|
||||
public static String generateVerifyCode(int verifySize, String sources)
|
||||
{
|
||||
if (sources == null || sources.length() == 0)
|
||||
{
|
||||
sources = VERIFY_CODES;
|
||||
}
|
||||
int codesLen = sources.length();
|
||||
Random rand = new Random(System.currentTimeMillis());
|
||||
StringBuilder verifyCode = new StringBuilder(verifySize);
|
||||
for (int i = 0; i < verifySize; i++)
|
||||
{
|
||||
verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
|
||||
}
|
||||
return verifyCode.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出指定验证码图片流
|
||||
*
|
||||
* @param w
|
||||
* @param h
|
||||
* @param os
|
||||
* @param code
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void outputImage(int w, int h, OutputStream os, String code) throws IOException
|
||||
{
|
||||
int verifySize = code.length();
|
||||
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
Random rand = new Random();
|
||||
Graphics2D g2 = image.createGraphics();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color[] colors = new Color[5];
|
||||
Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
|
||||
Color.ORANGE, Color.PINK, Color.YELLOW };
|
||||
float[] fractions = new float[colors.length];
|
||||
for (int i = 0; i < colors.length; i++)
|
||||
{
|
||||
colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
|
||||
fractions[i] = rand.nextFloat();
|
||||
}
|
||||
Arrays.sort(fractions);
|
||||
|
||||
g2.setColor(Color.GRAY);// 设置边框色
|
||||
g2.fillRect(0, 0, w, h);
|
||||
|
||||
Color c = getRandColor(200, 250);
|
||||
g2.setColor(c);// 设置背景色
|
||||
g2.fillRect(0, 2, w, h - 4);
|
||||
|
||||
// 绘制干扰线
|
||||
Random random = new Random();
|
||||
g2.setColor(getRandColor(160, 200));// 设置线条的颜色
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
int x = random.nextInt(w - 1);
|
||||
int y = random.nextInt(h - 1);
|
||||
int xl = random.nextInt(6) + 1;
|
||||
int yl = random.nextInt(12) + 1;
|
||||
g2.drawLine(x, y, x + xl + 40, y + yl + 20);
|
||||
}
|
||||
|
||||
// 添加噪点
|
||||
float yawpRate = 0.05f;// 噪声率
|
||||
int area = (int) (yawpRate * w * h);
|
||||
for (int i = 0; i < area; i++)
|
||||
{
|
||||
int x = random.nextInt(w);
|
||||
int y = random.nextInt(h);
|
||||
int rgb = getRandomIntColor();
|
||||
image.setRGB(x, y, rgb);
|
||||
}
|
||||
|
||||
shear(g2, w, h, c);// 使图片扭曲
|
||||
|
||||
g2.setColor(getRandColor(100, 160));
|
||||
int fontSize = h - 4;
|
||||
Font font = new Font("Algerian", Font.ITALIC, fontSize);
|
||||
g2.setFont(font);
|
||||
char[] chars = code.toCharArray();
|
||||
for (int i = 0; i < verifySize; i++)
|
||||
{
|
||||
AffineTransform affine = new AffineTransform();
|
||||
affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
|
||||
(w / verifySize) * i + fontSize / 2, h / 2);
|
||||
g2.setTransform(affine);
|
||||
g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
ImageIO.write(image, "jpg", os);
|
||||
}
|
||||
|
||||
private static Color getRandColor(int fc, int bc)
|
||||
{
|
||||
if (fc > 255) {
|
||||
fc = 255;
|
||||
}
|
||||
if (bc > 255) {
|
||||
bc = 255;
|
||||
}
|
||||
int r = fc + random.nextInt(bc - fc);
|
||||
int g = fc + random.nextInt(bc - fc);
|
||||
int b = fc + random.nextInt(bc - fc);
|
||||
return new Color(r, g, b);
|
||||
}
|
||||
|
||||
private static int getRandomIntColor()
|
||||
{
|
||||
int[] rgb = getRandomRgb();
|
||||
int color = 0;
|
||||
for (int c : rgb)
|
||||
{
|
||||
color = color << 8;
|
||||
color = color | c;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
private static int[] getRandomRgb()
|
||||
{
|
||||
int[] rgb = new int[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
rgb[i] = random.nextInt(255);
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
|
||||
private static void shear(Graphics g, int w1, int h1, Color color)
|
||||
{
|
||||
shearX(g, w1, h1, color);
|
||||
shearY(g, w1, h1, color);
|
||||
}
|
||||
|
||||
private static void shearX(Graphics g, int w1, int h1, Color color)
|
||||
{
|
||||
|
||||
int period = random.nextInt(2);
|
||||
|
||||
boolean borderGap = true;
|
||||
int frames = 1;
|
||||
int phase = random.nextInt(2);
|
||||
|
||||
for (int i = 0; i < h1; i++)
|
||||
{
|
||||
double d = (double) (period >> 1)
|
||||
* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
|
||||
g.copyArea(0, i, w1, 1, (int) d, 0);
|
||||
if (borderGap)
|
||||
{
|
||||
g.setColor(color);
|
||||
g.drawLine((int) d, i, 0, i);
|
||||
g.drawLine((int) d + w1, i, w1, i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void shearY(Graphics g, int w1, int h1, Color color)
|
||||
{
|
||||
|
||||
int period = random.nextInt(40) + 10; // 50;
|
||||
|
||||
boolean borderGap = true;
|
||||
int frames = 20;
|
||||
int phase = 7;
|
||||
for (int i = 0; i < w1; i++)
|
||||
{
|
||||
double d = (double) (period >> 1)
|
||||
* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
|
||||
g.copyArea(i, 0, 1, h1, 0, (int) d);
|
||||
if (borderGap)
|
||||
{
|
||||
g.setColor(color);
|
||||
g.drawLine(i, (int) d, i, 0);
|
||||
g.drawLine(i, (int) d + h1, i, h1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.fastbee.common.utils.bean;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Bean 工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class BeanUtils extends org.springframework.beans.BeanUtils
|
||||
{
|
||||
/** Bean方法名中属性名开始的下标 */
|
||||
private static final int BEAN_METHOD_PROP_INDEX = 3;
|
||||
|
||||
/** * 匹配getter方法的正则表达式 */
|
||||
private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/** * 匹配setter方法的正则表达式 */
|
||||
private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/**
|
||||
* Bean属性复制工具方法。
|
||||
*
|
||||
* @param dest 目标对象
|
||||
* @param src 源对象
|
||||
*/
|
||||
public static void copyBeanProp(Object dest, Object src)
|
||||
{
|
||||
try
|
||||
{
|
||||
copyProperties(src, dest);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的setter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 对象的setter方法列表
|
||||
*/
|
||||
public static List<Method> getSetterMethods(Object obj)
|
||||
{
|
||||
// setter方法列表
|
||||
List<Method> setterMethods = new ArrayList<Method>();
|
||||
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
|
||||
// 查找setter方法
|
||||
|
||||
for (Method method : methods)
|
||||
{
|
||||
Matcher m = SET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 1))
|
||||
{
|
||||
setterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回setter方法列表
|
||||
return setterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的getter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 对象的getter方法列表
|
||||
*/
|
||||
|
||||
public static List<Method> getGetterMethods(Object obj)
|
||||
{
|
||||
// getter方法列表
|
||||
List<Method> getterMethods = new ArrayList<Method>();
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
// 查找getter方法
|
||||
for (Method method : methods)
|
||||
{
|
||||
Matcher m = GET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 0))
|
||||
{
|
||||
getterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回getter方法列表
|
||||
return getterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Bean方法名中的属性名是否相等。<br>
|
||||
* 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
|
||||
*
|
||||
* @param m1 方法名1
|
||||
* @param m2 方法名2
|
||||
* @return 属性名一样返回true,否则返回false
|
||||
*/
|
||||
|
||||
public static boolean isMethodPropEquals(String m1, String m2)
|
||||
{
|
||||
return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.fastbee.common.utils.bean;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
|
||||
/**
|
||||
* bean对象属性验证
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class BeanValidators
|
||||
{
|
||||
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
|
||||
throws ConstraintViolationException
|
||||
{
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (!constraintViolations.isEmpty())
|
||||
{
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.fastbee.common.utils.collection;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/9/15 16:52
|
||||
*/
|
||||
public class CollectionUtils {
|
||||
|
||||
/*数组复制*/
|
||||
public static String[] copy(String[] source){
|
||||
if(isEmpty(source)){
|
||||
return null;
|
||||
}
|
||||
int len = source.length;
|
||||
String[] arr = new String[len];
|
||||
for(int i=0; i < len; i ++){
|
||||
arr[i] = source[i];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
/*数组连接*/
|
||||
public static String concat(String[] source, String split){
|
||||
if(isEmpty(source)){
|
||||
return null;
|
||||
}
|
||||
String result = "";
|
||||
for(int i=0; i < source.length; i ++){
|
||||
result = result.concat(source[i]);
|
||||
if(i != source.length - 1){
|
||||
result = result.concat(split);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(String[] source){
|
||||
if(null == source){
|
||||
return true;
|
||||
}
|
||||
if(0 == source.length){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean containsAny(Object source, Object... targets) {
|
||||
return Arrays.asList(targets).contains(source);
|
||||
}
|
||||
|
||||
public static boolean isAnyEmpty(Collection<?>... collections) {
|
||||
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
|
||||
}
|
||||
|
||||
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().filter(predicate).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return distinct(from, keyMapper, (t1, t2) -> t1);
|
||||
}
|
||||
|
||||
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, Function.identity());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return supplier.get();
|
||||
}
|
||||
return convertMap(from, keyFunc, Function.identity(), supplier);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return supplier.get();
|
||||
}
|
||||
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream()
|
||||
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
|
||||
}
|
||||
|
||||
// 暂时没想好名字,先以 2 结尾噶
|
||||
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
||||
}
|
||||
|
||||
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||
}
|
||||
|
||||
public static <T> T getFirst(List<T> from) {
|
||||
return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
|
||||
}
|
||||
|
||||
public static <T> T findFirst(List<T> from, Predicate<T> predicate) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
return from.stream().filter(predicate).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getMaxValue(List<T> from, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert from.size() > 0; // 断言,避免告警
|
||||
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert from.size() > 0; // 断言,避免告警
|
||||
T t = from.stream().min(Comparator.comparing(valueFunc)).get();
|
||||
return valueFunc.apply(t);
|
||||
}
|
||||
|
||||
public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return null;
|
||||
}
|
||||
assert from.size() > 0; // 断言,避免告警
|
||||
return from.stream().map(valueFunc).reduce(accumulator).get();
|
||||
}
|
||||
|
||||
public static <T> void addIfNotNull(Collection<T> coll, T item) {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
coll.add(item);
|
||||
}
|
||||
|
||||
public static <T> Collection<T> singleton(T deptId) {
|
||||
return deptId == null ? Collections.emptyList() : Collections.singleton(deptId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始分页
|
||||
*
|
||||
* @param list 传入的list集合
|
||||
* @param pageNum 页码
|
||||
* @param pageSize 每页多少条数据
|
||||
* @return
|
||||
*/
|
||||
public static List startPage(List list, Integer pageNum,
|
||||
Integer pageSize) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
if (list.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
Integer count = list.size(); // 记录总数
|
||||
Integer pageCount = 0; // 页数
|
||||
if (count % pageSize == 0) {
|
||||
pageCount = count / pageSize;
|
||||
} else {
|
||||
pageCount = count / pageSize + 1;
|
||||
}
|
||||
int fromIndex = 0; // 开始索引
|
||||
int toIndex = 0; // 结束索引
|
||||
if (!pageNum.equals(pageCount)) {
|
||||
fromIndex = (pageNum - 1) * pageSize;
|
||||
toIndex = fromIndex + pageSize;
|
||||
} else {
|
||||
fromIndex = (pageNum - 1) * pageSize;
|
||||
toIndex = count;
|
||||
}
|
||||
List pageList = list.subList(fromIndex, toIndex);
|
||||
return pageList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.fastbee.common.utils.date;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author fastbee
|
||||
*/
|
||||
public class DateUtils {
|
||||
|
||||
/**
|
||||
* 时区 - 默认
|
||||
*/
|
||||
public static final String TIME_ZONE_DEFAULT = "GMT+8";
|
||||
|
||||
/**
|
||||
* 秒转换成毫秒
|
||||
*/
|
||||
public static final long SECOND_MILLIS = 1000;
|
||||
|
||||
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 将 LocalDateTime 转换成 Date
|
||||
*
|
||||
* @param date LocalDateTime
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static Date of(LocalDateTime date) {
|
||||
// 将此日期时间与时区相结合以创建 ZonedDateTime
|
||||
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
|
||||
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
|
||||
Instant instant = zonedDateTime.toInstant();
|
||||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Date 转换成 LocalDateTime
|
||||
*
|
||||
* @param date Date
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime of(Date date) {
|
||||
// 转为时间戳
|
||||
Instant instant = date.toInstant();
|
||||
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Date addTime(Duration duration) {
|
||||
return new Date(System.currentTimeMillis() + duration.toMillis());
|
||||
}
|
||||
|
||||
public static boolean isExpired(Date time) {
|
||||
return System.currentTimeMillis() > time.getTime();
|
||||
}
|
||||
|
||||
public static boolean isExpired(LocalDateTime time) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return now.isAfter(time);
|
||||
}
|
||||
|
||||
public static long diff(Date endTime, Date startTime) {
|
||||
return endTime.getTime() - startTime.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param mouth 月
|
||||
* @param day 日
|
||||
* @return 指定时间
|
||||
*/
|
||||
public static Date buildTime(int year, int mouth, int day) {
|
||||
return buildTime(year, mouth, day, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param mouth 月
|
||||
* @param day 日
|
||||
* @param hour 小时
|
||||
* @param minute 分钟
|
||||
* @param second 秒
|
||||
* @return 指定时间
|
||||
*/
|
||||
public static Date buildTime(int year, int mouth, int day,
|
||||
int hour, int minute, int second) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.YEAR, year);
|
||||
calendar.set(Calendar.MONTH, mouth - 1);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, second);
|
||||
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public static Date max(Date a, Date b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
}
|
||||
if (b == null) {
|
||||
return a;
|
||||
}
|
||||
return a.compareTo(b) > 0 ? a : b;
|
||||
}
|
||||
|
||||
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
}
|
||||
if (b == null) {
|
||||
return a;
|
||||
}
|
||||
return a.isAfter(b) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当期时间相差的日期
|
||||
*
|
||||
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
|
||||
* @param amount 相差的数值
|
||||
* @return 计算后的日志
|
||||
*/
|
||||
public static Date addDate(int field, int amount) {
|
||||
return addDate(null, field, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当期时间相差的日期
|
||||
*
|
||||
* @param date 设置时间
|
||||
* @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等
|
||||
* @param amount 相差的数值
|
||||
* @return 计算后的日志
|
||||
*/
|
||||
public static Date addDate(Date date, int field, int amount) {
|
||||
if (amount == 0) {
|
||||
return date;
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
if (date != null) {
|
||||
c.setTime(date);
|
||||
}
|
||||
c.add(field, amount);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否今天
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isToday(LocalDateTime date) {
|
||||
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.fastbee.common.utils.date;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于 {@link LocalDateTime}
|
||||
*
|
||||
* @author fastbee
|
||||
*/
|
||||
public class LocalDateTimeUtils {
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
|
||||
*/
|
||||
public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
|
||||
|
||||
public static LocalDateTime addTime(Duration duration) {
|
||||
return LocalDateTime.now().plus(duration);
|
||||
}
|
||||
|
||||
public static boolean beforeNow(LocalDateTime date) {
|
||||
return date.isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static boolean afterNow(LocalDateTime date) {
|
||||
return date.isAfter(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定时间
|
||||
*
|
||||
* @param year 年
|
||||
* @param mouth 月
|
||||
* @param day 日
|
||||
* @return 指定时间
|
||||
*/
|
||||
public static LocalDateTime buildTime(int year, int mouth, int day) {
|
||||
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
|
||||
}
|
||||
|
||||
public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
|
||||
int year2, int mouth2, int day2) {
|
||||
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间是否在该时间范围内
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
if (startTime == null || endTime == null) {
|
||||
return false;
|
||||
}
|
||||
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转字符串
|
||||
* @param localDateTime 时间
|
||||
* @param: format 格式
|
||||
* @return java.lang.String
|
||||
*/
|
||||
public static String localDateTimeToStr(LocalDateTime localDateTime, String format) {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
|
||||
return localDateTime.format(dateTimeFormatter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.fastbee.common.utils.file;
|
||||
|
||||
import java.io.File;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 文件类型工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileTypeUtils
|
||||
{
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: fastbee.txt, 返回: txt
|
||||
*
|
||||
* @param file 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(File file)
|
||||
{
|
||||
if (null == file)
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getFileType(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: fastbee.txt, 返回: txt
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(String fileName)
|
||||
{
|
||||
int separatorIndex = fileName.lastIndexOf(".");
|
||||
if (separatorIndex < 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(separatorIndex + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @param photoByte 文件字节码
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileExtendName(byte[] photoByte)
|
||||
{
|
||||
String strFileExtendName = "JPG";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
|
||||
{
|
||||
strFileExtendName = "GIF";
|
||||
}
|
||||
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
|
||||
{
|
||||
strFileExtendName = "JPG";
|
||||
}
|
||||
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
|
||||
{
|
||||
strFileExtendName = "BMP";
|
||||
}
|
||||
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
|
||||
{
|
||||
strFileExtendName = "PNG";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.fastbee.common.utils.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.fastbee.common.config.RuoYiConfig;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.exception.file.FileNameLengthLimitExceededException;
|
||||
import com.fastbee.common.exception.file.FileSizeLimitExceededException;
|
||||
import com.fastbee.common.exception.file.InvalidExtensionException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.uuid.Seq;
|
||||
|
||||
/**
|
||||
* 文件上传工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileUploadUtils
|
||||
{
|
||||
/**
|
||||
* 默认大小 50M
|
||||
*/
|
||||
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* 默认的文件名最大长度 100
|
||||
*/
|
||||
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
|
||||
|
||||
/**
|
||||
* 默认上传的地址
|
||||
*/
|
||||
private static String defaultBaseDir = RuoYiConfig.getProfile();
|
||||
|
||||
public static void setDefaultBaseDir(String defaultBaseDir)
|
||||
{
|
||||
FileUploadUtils.defaultBaseDir = defaultBaseDir;
|
||||
}
|
||||
|
||||
public static String getDefaultBaseDir()
|
||||
{
|
||||
return defaultBaseDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以默认配置进行文件上传
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws Exception
|
||||
*/
|
||||
public static final String upload(MultipartFile file) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @return 文件名称
|
||||
* @throws IOException
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param baseDir 相对应用的基目录
|
||||
* @param file 上传的文件
|
||||
* @param allowedExtension 上传文件类型
|
||||
* @return 返回上传成功的文件名
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
* @throws FileNameLengthLimitExceededException 文件名太长
|
||||
* @throws IOException 比如读写文件出错时
|
||||
* @throws InvalidExtensionException 文件校验异常
|
||||
*/
|
||||
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
|
||||
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
|
||||
InvalidExtensionException
|
||||
{
|
||||
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
|
||||
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
|
||||
{
|
||||
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
|
||||
}
|
||||
|
||||
assertAllowed(file, allowedExtension);
|
||||
|
||||
String fileName = extractFilename(file);
|
||||
|
||||
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
|
||||
file.transferTo(Paths.get(absPath));
|
||||
return getPathFileName(baseDir, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码文件名
|
||||
*/
|
||||
public static final String extractFilename(MultipartFile file)
|
||||
{
|
||||
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
|
||||
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
|
||||
}
|
||||
|
||||
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
|
||||
{
|
||||
File desc = new File(uploadDir + File.separator + fileName);
|
||||
|
||||
if (!desc.exists())
|
||||
{
|
||||
if (!desc.getParentFile().exists())
|
||||
{
|
||||
desc.getParentFile().mkdirs();
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static final String getPathFileName(String uploadDir, String fileName) throws IOException
|
||||
{
|
||||
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
|
||||
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
|
||||
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件大小校验
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @return
|
||||
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||
* @throws InvalidExtensionException
|
||||
*/
|
||||
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
|
||||
throws FileSizeLimitExceededException, InvalidExtensionException
|
||||
{
|
||||
long size = file.getSize();
|
||||
if (size > DEFAULT_MAX_SIZE)
|
||||
{
|
||||
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
|
||||
}
|
||||
|
||||
String fileName = file.getOriginalFilename();
|
||||
String extension = getExtension(file);
|
||||
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
|
||||
{
|
||||
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
|
||||
{
|
||||
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
}
|
||||
else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
|
||||
{
|
||||
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
}
|
||||
else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
|
||||
{
|
||||
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
}
|
||||
else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
|
||||
{
|
||||
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
|
||||
fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidExtensionException(allowedExtension, extension, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断MIME类型是否是允许的MIME类型
|
||||
*
|
||||
* @param extension
|
||||
* @param allowedExtension
|
||||
* @return
|
||||
*/
|
||||
public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
|
||||
{
|
||||
for (String str : allowedExtension)
|
||||
{
|
||||
if (str.equalsIgnoreCase(extension))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名的后缀
|
||||
*
|
||||
* @param file 表单文件
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static final String getExtension(MultipartFile file)
|
||||
{
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
if (StringUtils.isEmpty(extension))
|
||||
{
|
||||
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
package com.fastbee.common.utils.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import com.fastbee.common.config.RuoYiConfig;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.uuid.IdUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
/**
|
||||
* 文件处理工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileUtils
|
||||
{
|
||||
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
|
||||
|
||||
/**
|
||||
* 输出指定文件的byte数组
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @param os 输出流
|
||||
* @return
|
||||
*/
|
||||
public static void writeBytes(String filePath, OutputStream os) throws IOException
|
||||
{
|
||||
FileInputStream fis = null;
|
||||
try
|
||||
{
|
||||
File file = new File(filePath);
|
||||
if (!file.exists())
|
||||
{
|
||||
throw new FileNotFoundException(filePath);
|
||||
}
|
||||
fis = new FileInputStream(file);
|
||||
byte[] b = new byte[1024];
|
||||
int length;
|
||||
while ((length = fis.read(b)) > 0)
|
||||
{
|
||||
os.write(b, 0, length);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.close(os);
|
||||
IOUtils.close(fis);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写数据到文件中
|
||||
*
|
||||
* @param data 数据
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static String writeImportBytes(byte[] data) throws IOException
|
||||
{
|
||||
return writeBytes(data, RuoYiConfig.getImportPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 写数据到文件中
|
||||
*
|
||||
* @param data 数据
|
||||
* @param uploadDir 目标文件
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public static String writeBytes(byte[] data, String uploadDir) throws IOException
|
||||
{
|
||||
FileOutputStream fos = null;
|
||||
String pathName = "";
|
||||
try
|
||||
{
|
||||
String extension = getFileExtendName(data);
|
||||
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
|
||||
File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write(data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.close(fos);
|
||||
}
|
||||
return FileUploadUtils.getPathFileName(uploadDir, pathName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param filePath 文件
|
||||
* @return
|
||||
*/
|
||||
public static boolean deleteFile(String filePath)
|
||||
{
|
||||
boolean flag = false;
|
||||
File file = new File(filePath);
|
||||
// 路径为文件且不为空则进行删除
|
||||
if (file.isFile() && file.exists())
|
||||
{
|
||||
flag = file.delete();
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件名称验证
|
||||
*
|
||||
* @param filename 文件名称
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean isValidFilename(String filename)
|
||||
{
|
||||
return filename.matches(FILENAME_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否可下载
|
||||
*
|
||||
* @param resource 需要下载的文件
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean checkAllowDownload(String resource)
|
||||
{
|
||||
// 禁止目录上跳级别
|
||||
if (StringUtils.contains(resource, ".."))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查允许下载的文件规则
|
||||
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 不在允许下载的文件规则
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param fileName 文件名
|
||||
* @return 编码后的文件名
|
||||
*/
|
||||
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
|
||||
{
|
||||
final String agent = request.getHeader("USER-AGENT");
|
||||
String filename = fileName;
|
||||
if (agent.contains("MSIE"))
|
||||
{
|
||||
// IE浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
filename = filename.replace("+", " ");
|
||||
}
|
||||
else if (agent.contains("Firefox"))
|
||||
{
|
||||
// 火狐浏览器
|
||||
filename = new String(fileName.getBytes(), "ISO8859-1");
|
||||
}
|
||||
else if (agent.contains("Chrome"))
|
||||
{
|
||||
// google浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其它浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param realFileName 真实文件名
|
||||
*/
|
||||
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
|
||||
{
|
||||
String percentEncodedFileName = percentEncode(realFileName);
|
||||
|
||||
StringBuilder contentDispositionValue = new StringBuilder();
|
||||
contentDispositionValue.append("attachment; filename=")
|
||||
.append(percentEncodedFileName)
|
||||
.append(";")
|
||||
.append("filename*=")
|
||||
.append("utf-8''")
|
||||
.append(percentEncodedFileName);
|
||||
|
||||
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
|
||||
response.setHeader("Content-disposition", contentDispositionValue.toString());
|
||||
response.setHeader("download-filename", percentEncodedFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百分号编码工具方法
|
||||
*
|
||||
* @param s 需要百分号编码的字符串
|
||||
* @return 百分号编码后的字符串
|
||||
*/
|
||||
public static String percentEncode(String s) throws UnsupportedEncodingException
|
||||
{
|
||||
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
|
||||
return encode.replaceAll("\\+", "%20");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像后缀
|
||||
*
|
||||
* @param photoByte 图像数据
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static String getFileExtendName(byte[] photoByte)
|
||||
{
|
||||
String strFileExtendName = "jpg";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
|
||||
{
|
||||
strFileExtendName = "gif";
|
||||
}
|
||||
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
|
||||
{
|
||||
strFileExtendName = "jpg";
|
||||
}
|
||||
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
|
||||
{
|
||||
strFileExtendName = "bmp";
|
||||
}
|
||||
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
|
||||
{
|
||||
strFileExtendName = "png";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
|
||||
*
|
||||
* @param fileName 路径名称
|
||||
* @return 没有文件路径的名称
|
||||
*/
|
||||
public static String getName(String fileName)
|
||||
{
|
||||
if (fileName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int lastUnixPos = fileName.lastIndexOf('/');
|
||||
int lastWindowsPos = fileName.lastIndexOf('\\');
|
||||
int index = Math.max(lastUnixPos, lastWindowsPos);
|
||||
return fileName.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
|
||||
*
|
||||
* @param fileName 路径名称
|
||||
* @return 没有文件路径和后缀的名称
|
||||
*/
|
||||
public static String getNameNotSuffix(String fileName)
|
||||
{
|
||||
if (fileName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String baseName = FilenameUtils.getBaseName(fileName);
|
||||
return baseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件
|
||||
* 该文件会在 JVM 退出时,进行删除
|
||||
*
|
||||
* @param data 文件内容
|
||||
* @return 文件
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static File createTempFile(String data) {
|
||||
File file = createTempFile();
|
||||
// 写入内容
|
||||
FileUtil.writeUtf8String(data, file);
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件
|
||||
* 该文件会在 JVM 退出时,进行删除
|
||||
*
|
||||
* @param data 文件内容
|
||||
* @return 文件
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static File createTempFile(byte[] data) {
|
||||
File file = createTempFile();
|
||||
// 写入内容
|
||||
FileUtil.writeBytes(data, file);
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件,无内容
|
||||
* 该文件会在 JVM 退出时,进行删除
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static File createTempFile() {
|
||||
// 创建文件,通过 UUID 保证唯一
|
||||
File file = File.createTempFile(IdUtil.simpleUUID(), null);
|
||||
// 标记 JVM 退出时,自动删除
|
||||
file.deleteOnExit();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.fastbee.common.utils.file;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.fastbee.common.config.RuoYiConfig;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 图片处理工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ImageUtils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
|
||||
|
||||
public static byte[] getImage(String imagePath)
|
||||
{
|
||||
InputStream is = getFile(imagePath);
|
||||
try
|
||||
{
|
||||
return IOUtils.toByteArray(is);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("图片加载异常 {}", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getFile(String imagePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] result = readFile(imagePath);
|
||||
result = Arrays.copyOf(result, result.length);
|
||||
return new ByteArrayInputStream(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取图片异常 {}", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数据
|
||||
*
|
||||
* @param url 地址
|
||||
* @return 字节数据
|
||||
*/
|
||||
public static byte[] readFile(String url)
|
||||
{
|
||||
InputStream in = null;
|
||||
try
|
||||
{
|
||||
if (url.startsWith("http"))
|
||||
{
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 本机地址
|
||||
String localPath = RuoYiConfig.getProfile();
|
||||
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
|
||||
in = new FileInputStream(downloadPath);
|
||||
}
|
||||
return IOUtils.toByteArray(in);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取文件路径异常 {}", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.fastbee.common.utils.file;
|
||||
|
||||
/**
|
||||
* 媒体类型工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class MimeTypeUtils
|
||||
{
|
||||
public static final String IMAGE_PNG = "image/png";
|
||||
|
||||
public static final String IMAGE_JPG = "image/jpg";
|
||||
|
||||
public static final String IMAGE_JPEG = "image/jpeg";
|
||||
|
||||
public static final String IMAGE_BMP = "image/bmp";
|
||||
|
||||
public static final String IMAGE_GIF = "image/gif";
|
||||
|
||||
public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
|
||||
|
||||
public static final String[] FLASH_EXTENSION = { "swf", "flv" };
|
||||
|
||||
public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
|
||||
"asf", "rm", "rmvb" };
|
||||
|
||||
public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
|
||||
|
||||
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
|
||||
// 图片
|
||||
"bmp", "gif", "jpg", "jpeg", "png",
|
||||
// word excel powerpoint
|
||||
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
|
||||
// 压缩文件
|
||||
"rar", "zip", "gz", "bz2",
|
||||
// 视频格式
|
||||
"mp4", "avi", "rmvb",
|
||||
// pdf
|
||||
"pdf" };
|
||||
|
||||
public static String getExtension(String prefix)
|
||||
{
|
||||
switch (prefix)
|
||||
{
|
||||
case IMAGE_PNG:
|
||||
return "png";
|
||||
case IMAGE_JPG:
|
||||
return "jpg";
|
||||
case IMAGE_JPEG:
|
||||
return "jpeg";
|
||||
case IMAGE_BMP:
|
||||
return "bmp";
|
||||
case IMAGE_GIF:
|
||||
return "gif";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.fastbee.common.utils.gateway;
|
||||
|
||||
|
||||
import com.fastbee.common.utils.CaculateUtils;
|
||||
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
public class CRC16Utils {
|
||||
|
||||
//ff
|
||||
private static int CRC_FF = 0x000000ff;
|
||||
//01
|
||||
private static int CRC_01 = 0x00000001;
|
||||
//04
|
||||
private static final int LENGTH_04 = 4;
|
||||
//16进制
|
||||
private static final int OXFF = 0xff;
|
||||
|
||||
/**
|
||||
* 低位在前,高位在后
|
||||
*
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static String getCRC(byte[] bytes) {
|
||||
return getCRC(bytes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes
|
||||
* @param lb 是否低位在前, 高位在后
|
||||
* @return
|
||||
*/
|
||||
public static String getCRC(byte[] bytes, boolean lb) {
|
||||
int CRC = 0x0000ffff;
|
||||
int POLYNOMIAL = 0x0000a001;
|
||||
|
||||
int i, j;
|
||||
for (i = 0; i < bytes.length; i++) {
|
||||
CRC ^= ((int) bytes[i] & 0x000000ff);
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((CRC & 0x00000001) != 0) {
|
||||
CRC >>= 1;
|
||||
CRC ^= POLYNOMIAL;
|
||||
} else {
|
||||
CRC >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//结果转换为16进制
|
||||
String result = Integer.toHexString(CRC).toUpperCase();
|
||||
if (result.length() != 4) {
|
||||
StringBuffer sb = new StringBuffer("0000");
|
||||
result = sb.replace(4 - result.length(), 4, result).toString();
|
||||
}
|
||||
|
||||
if (lb) { // 低位在前, 高位在后
|
||||
result = result.substring(2, 4) + result.substring(0, 2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算CRC校验和
|
||||
*
|
||||
* @param bytes
|
||||
* @return 返回 byte[]
|
||||
*/
|
||||
public static byte[] getCrc16Byte(byte[] bytes) {
|
||||
|
||||
//寄存器全为1
|
||||
int CRC_16 = 0x0000ffff;
|
||||
// 多项式校验值
|
||||
int POLYNOMIAL = 0x0000a001;
|
||||
for (byte aByte : bytes) {
|
||||
CRC_16 ^= ((int) aByte & CRC_FF);
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((CRC_16 & CRC_01) != 0) {
|
||||
CRC_16 >>= 1;
|
||||
CRC_16 ^= POLYNOMIAL;
|
||||
} else {
|
||||
CRC_16 >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 低8位 ,高8位
|
||||
return new byte[]{(byte) (CRC_16 & OXFF), (byte) (CRC_16 >> 8 & OXFF)};
|
||||
}
|
||||
|
||||
public static byte[] AddCRC(byte[] source) {
|
||||
byte[] result = new byte[source.length + 2];
|
||||
byte[] crc16Byte = CRC16Utils.getCrc16Byte(source);
|
||||
System.arraycopy(source, 0, result, 0, source.length);
|
||||
System.arraycopy(crc16Byte, 0, result, result.length - 2, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] AddPakCRC(byte[] source) {
|
||||
byte[] subarray = ArrayUtils.subarray(source, 11, source.length);
|
||||
byte[] result = new byte[source.length + 2];
|
||||
byte[] crc16Byte = CRC16Utils.getCrc16Byte(subarray);
|
||||
System.arraycopy(source, 0, result, 0, source.length);
|
||||
System.arraycopy(crc16Byte, 0, result, result.length - 2, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] CRC(byte[] source) {
|
||||
source[2] = (byte) ((int) source[2] * 2);
|
||||
byte[] result = new byte[source.length + 2];
|
||||
byte[] crc16Byte = CRC16Utils.getCrc16Byte(source);
|
||||
System.arraycopy(source, 0, result, 0, source.length);
|
||||
System.arraycopy(crc16Byte, 0, result, result.length - 2, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte CRC8(byte[] buffer) {
|
||||
int crci = 0xFF; //起始字节FF
|
||||
for (int j = 0; j < buffer.length; j++) {
|
||||
crci ^= buffer[j] & 0xFF;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((crci & 1) != 0) {
|
||||
crci >>= 1;
|
||||
crci ^= 0xB8; //多项式当中的那个啥的,不同多项式不一样
|
||||
} else {
|
||||
crci >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (byte) crci;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args)throws Exception {
|
||||
String hex = "800041EE";
|
||||
byte[] bytes = ByteUtils.hexToByte(hex);
|
||||
String crc = getCRC(bytes);
|
||||
System.out.println(crc);
|
||||
String crc8 = "680868333701120008C100";
|
||||
byte[] byte8 = ByteUtils.hexToByte(crc8);
|
||||
int b = CRC8(byte8);
|
||||
System.out.println((int) b);
|
||||
float v = CaculateUtils.toFloat32_CDAB(bytes);
|
||||
System.out.println(v);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.fastbee.common.utils.gateway;
|
||||
|
||||
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/5/19 11:33
|
||||
*/
|
||||
public class CRC8Utils {
|
||||
|
||||
|
||||
//TODO:-----------------根据C写法转译--------------------------------------
|
||||
/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */
|
||||
|
||||
/**
|
||||
* CRC8 校验 多项式 x8+x2+x+1
|
||||
* @param data
|
||||
* @return 校验和
|
||||
*/
|
||||
public static byte calcCrc8_E5(byte[] data){
|
||||
byte crc = 0;
|
||||
for (int j = 0; j < data.length; j++) {
|
||||
crc ^= data[j];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((crc & 0x80) != 0) {
|
||||
crc = (byte) ((crc)<< 1);
|
||||
crc ^= 0xE5;
|
||||
} else {
|
||||
crc = (byte) ((crc)<< 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static byte[] crc8_tab = {(byte) 0, (byte) 94, (byte) 188, (byte) 226, (byte) 97, (byte) 63, (byte) 221, (byte) 131, (byte) 194, (byte) 156, (byte) 126, (byte) 32, (byte) 163, (byte) 253, (byte) 31, (byte) 65, (byte) 157, (byte) 195, (byte) 33, (byte) 127, (byte) 252, (byte) 162, (byte) 64, (byte) 30, (byte) 95, (byte) 1, (byte) 227, (byte) 189, (byte) 62, (byte) 96, (byte) 130, (byte) 220, (byte) 35, (byte) 125, (byte) 159, (byte) 193, (byte) 66, (byte) 28, (byte) 254, (byte) 160, (byte) 225, (byte) 191, (byte) 93, (byte) 3, (byte) 128, (byte) 222, (byte) 60, (byte) 98, (byte) 190, (byte) 224, (byte) 2, (byte) 92, (byte) 223, (byte) 129, (byte) 99, (byte) 61, (byte) 124, (byte) 34, (byte) 192, (byte) 158, (byte) 29, (byte) 67, (byte) 161, (byte) 255, (byte) 70, (byte) 24,
|
||||
(byte) 250, (byte) 164, (byte) 39, (byte) 121, (byte) 155, (byte) 197, (byte) 132, (byte) 218, (byte) 56, (byte) 102, (byte) 229, (byte) 187, (byte) 89, (byte) 7, (byte) 219, (byte) 133, (byte) 103, (byte) 57, (byte) 186, (byte) 228, (byte) 6, (byte) 88, (byte) 25, (byte) 71, (byte) 165, (byte) 251, (byte) 120, (byte) 38, (byte) 196, (byte) 154, (byte) 101, (byte) 59, (byte) 217, (byte) 135, (byte) 4, (byte) 90, (byte) 184, (byte) 230, (byte) 167, (byte) 249, (byte) 27, (byte) 69, (byte) 198, (byte) 152, (byte) 122, (byte) 36, (byte) 248, (byte) 166, (byte) 68, (byte) 26, (byte) 153, (byte) 199, (byte) 37, (byte) 123, (byte) 58, (byte) 100, (byte) 134, (byte) 216, (byte) 91, (byte) 5, (byte) 231, (byte) 185, (byte) 140, (byte) 210, (byte) 48, (byte) 110, (byte) 237,
|
||||
(byte) 179, (byte) 81, (byte) 15, (byte) 78, (byte) 16, (byte) 242, (byte) 172, (byte) 47, (byte) 113, (byte) 147, (byte) 205, (byte) 17, (byte) 79, (byte) 173, (byte) 243, (byte) 112, (byte) 46, (byte) 204, (byte) 146, (byte) 211, (byte) 141, (byte) 111, (byte) 49, (byte) 178, (byte) 236, (byte) 14, (byte) 80, (byte) 175, (byte) 241, (byte) 19, (byte) 77, (byte) 206, (byte) 144, (byte) 114, (byte) 44, (byte) 109, (byte) 51, (byte) 209, (byte) 143, (byte) 12, (byte) 82, (byte) 176, (byte) 238, (byte) 50, (byte) 108, (byte) 142, (byte) 208, (byte) 83, (byte) 13, (byte) 239, (byte) 177, (byte) 240, (byte) 174, (byte) 76, (byte) 18, (byte) 145, (byte) 207, (byte) 45, (byte) 115, (byte) 202, (byte) 148, (byte) 118, (byte) 40, (byte) 171, (byte) 245, (byte) 23, (byte) 73, (byte) 8,
|
||||
(byte) 86, (byte) 180, (byte) 234, (byte) 105, (byte) 55, (byte) 213, (byte) 139, (byte) 87, (byte) 9, (byte) 235, (byte) 181, (byte) 54, (byte) 104, (byte) 138, (byte) 212, (byte) 149, (byte) 203, (byte) 41, (byte) 119, (byte) 244, (byte) 170, (byte) 72, (byte) 22, (byte) 233, (byte) 183, (byte) 85, (byte) 11, (byte) 136, (byte) 214, (byte) 52, (byte) 106, (byte) 43, (byte) 117, (byte) 151, (byte) 201, (byte) 74, (byte) 20, (byte) 246, (byte) 168, (byte) 116, (byte) 42, (byte) 200, (byte) 150, (byte) 21, (byte) 75, (byte) 169, (byte) 247, (byte) 182, (byte) 232, (byte) 10, (byte) 84, (byte) 215, (byte) 137, (byte) 107, 53};
|
||||
|
||||
/**
|
||||
* 计算数组的CRC8校验值
|
||||
*
|
||||
* @param data 需要计算的数组
|
||||
* @return CRC8校验值
|
||||
*/
|
||||
public static byte calcCrc8(byte[] data) {
|
||||
return calcCrc8(data, 0, data.length, (byte) 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算CRC8校验值
|
||||
*
|
||||
* @param data 数据
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @return 校验值
|
||||
*/
|
||||
public static byte calcCrc8(byte[] data, int offset, int len) {
|
||||
return calcCrc8(data, offset, len, (byte) 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算CRC8校验值
|
||||
*
|
||||
* @param data 数据
|
||||
* @param offset 起始位置
|
||||
* @param len 长度
|
||||
* @param preval 之前的校验值
|
||||
* @return 校验值
|
||||
*/
|
||||
public static byte calcCrc8(byte[] data, int offset, int len, byte preval) {
|
||||
byte ret = preval;
|
||||
for (int i = offset; i < (offset + len); ++i) {
|
||||
ret = crc8_tab[(0x00ff & (ret ^ data[i]))];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 测试
|
||||
public static void main(String[] args) {
|
||||
String hex = "333701120008C100";
|
||||
byte[] bytes = ByteUtils.hexToByte(hex);
|
||||
byte crc = CRC8Utils.calcCrc8_E5(bytes);
|
||||
System.out.println("" + Integer.toHexString(0x00ff & crc));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.fastbee.common.utils.gateway.mq;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Data
|
||||
public class Topics {
|
||||
|
||||
|
||||
private String topicName;
|
||||
private Integer qos =0;
|
||||
private String desc;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.fastbee.common.utils.gateway.mq;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2023/2/27 13:41
|
||||
*/
|
||||
@Data
|
||||
public class TopicsPost {
|
||||
|
||||
private String[] topics;
|
||||
private int[] qos;
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package com.fastbee.common.utils.gateway.mq;
|
||||
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.enums.TopicType;
|
||||
import com.fastbee.common.utils.collection.CollectionUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* topic工具类
|
||||
*
|
||||
* @author gsb
|
||||
* @date 2022/9/15 16:49
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TopicsUtils {
|
||||
|
||||
@Value("${server.broker.enabled}")
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 拼接topic
|
||||
*
|
||||
* @param productId 产品id
|
||||
* @param serialNumber 设备编号
|
||||
* @param type 主题类型
|
||||
* @return topic
|
||||
*/
|
||||
public String buildTopic(Long productId, String serialNumber, TopicType type) {
|
||||
/*
|
||||
* 订阅属性:
|
||||
* 如果启动emq 则为 /+/+/property/post
|
||||
* 如果启动netty的mqttBroker 则为 /{productId}/{serialNumber}/property/post
|
||||
*
|
||||
* 发布都为:/{productId}/{serialNumber}/property/get
|
||||
*/
|
||||
String product = String.valueOf(productId);
|
||||
if (null == productId || productId == -1L || productId == 0L) {
|
||||
product = "+";
|
||||
}
|
||||
if (com.fastbee.common.utils.StringUtils.isEmpty(serialNumber)) {
|
||||
serialNumber = "+";
|
||||
}
|
||||
if (type.getType() == 0) {
|
||||
return enabled ? "/" + product + "/" + serialNumber + type.getTopicSuffix() : FastBeeConstant.MQTT.PREDIX + type.getTopicSuffix();
|
||||
} else {
|
||||
return "/" + product + "/" + serialNumber + type.getTopicSuffix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有可订阅的主题
|
||||
*
|
||||
* @return 订阅主题列表
|
||||
*/
|
||||
public TopicsPost getAllPost() {
|
||||
List<Integer> qos = new ArrayList<>();
|
||||
List<String> topics = new ArrayList<>();
|
||||
TopicsPost post = new TopicsPost();
|
||||
for (TopicType topicType : TopicType.values()) {
|
||||
if (topicType.getType() == 0) {
|
||||
String topic = this.buildTopic(0L, null, topicType);
|
||||
topics.add(topic);
|
||||
qos.add(1);
|
||||
}
|
||||
}
|
||||
post.setTopics(topics.toArray(new String[0]));
|
||||
int[] ints = Arrays.stream(qos.toArray(new Integer[0])).mapToInt(Integer::valueOf).toArray();
|
||||
post.setQos(ints);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有get topic
|
||||
*
|
||||
* @param isSimulate 是否是设备模拟
|
||||
* @return list
|
||||
*/
|
||||
public static List<Topics> getAllGet(boolean isSimulate) {
|
||||
List<Topics> result = new ArrayList<>();
|
||||
for (TopicType type : TopicType.values()) {
|
||||
if (type.getType() == 4) {
|
||||
Topics topics = new Topics();
|
||||
topics.setTopicName(type.getTopicSuffix());
|
||||
topics.setDesc(type.getMsg());
|
||||
topics.setQos(1);
|
||||
result.add(topics);
|
||||
if (isSimulate && type == TopicType.PROPERTY_GET) {
|
||||
result.remove(topics);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换topic中的产品编码和设备编码,唯一作用是在系统收到来自网关设备上报子设备消息时将topic进行替换
|
||||
*
|
||||
* @param orgTopic String 原始topic
|
||||
* @param productId String 目标产品编码
|
||||
* @param serialNumber String 目标设备编码
|
||||
* @return 替换产品编码和设备编码后的新topic
|
||||
*/
|
||||
public String topicSubDevice(String orgTopic, Long productId, String serialNumber) {
|
||||
if (com.fastbee.common.utils.StringUtils.isEmpty(orgTopic)) {
|
||||
return orgTopic;
|
||||
}
|
||||
String[] splits = orgTopic.split("/");
|
||||
StringBuilder sb = new StringBuilder(splits[0])
|
||||
.append("/")
|
||||
.append(productId)
|
||||
.append("/")
|
||||
.append(serialNumber);
|
||||
for (int index = 3; index < splits.length; index++) {
|
||||
sb.append("/").append(splits[index]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从topic中获取IMEI号 IMEI即是设备编号
|
||||
*
|
||||
* @param topic /{productId}/{serialNumber}/property/post
|
||||
* @return serialNumber
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Long parseProductId(String topic) {
|
||||
String[] values = topic.split("/");
|
||||
return Long.parseLong(values[1]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从topic中获取IMEI号 IMEI即是设备编号
|
||||
*
|
||||
* @param topic /{productId}/{serialNumber}/property/post
|
||||
* @return serialNumber
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String parseSerialNumber(String topic) {
|
||||
String[] values = topic.split("/");
|
||||
return values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取topic 判断字段 name
|
||||
**/
|
||||
public String parseTopicName(String topic) {
|
||||
String[] values = topic.split("/");
|
||||
if (values.length >2){
|
||||
return "/"+ values[3] + "/" + values[4];
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取topic 判断字段 name
|
||||
**/
|
||||
public String parseTopicName4(String topic) {
|
||||
String[] values = topic.split("/");
|
||||
return values[4];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从topic解析物模型类型
|
||||
*
|
||||
* @param topic /{productId}/{serialNumber}/property/post
|
||||
* @return 物模型类型
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String getThingsModel(String topic) {
|
||||
String[] split = topic.split("/");
|
||||
return split[2].toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查topic的合法性
|
||||
*
|
||||
* @param topicNameList 主题list
|
||||
* @return 验证结果
|
||||
*/
|
||||
public static boolean validTopicFilter(List<String> topicNameList) {
|
||||
for (String topicName : topicNameList) {
|
||||
if (com.fastbee.common.utils.StringUtils.isEmpty(topicName)) {
|
||||
return false;
|
||||
}
|
||||
/*以#或+符号开头的、以/符号结尾的及不存在/符号的订阅按非法订阅处理*/
|
||||
if (StringUtils.startsWithIgnoreCase(topicName, "#") || StringUtils.startsWithIgnoreCase(topicName, "+") || StringUtils.endsWithIgnoreCase(topicName, "/") || !topicName.contains("/")) {
|
||||
return false;
|
||||
}
|
||||
if (topicName.contains("#")) {
|
||||
/*不是以/#字符串结尾的订阅按非法订阅处理*/
|
||||
if (!StringUtils.endsWithIgnoreCase(topicName, "/#")) {
|
||||
return false;
|
||||
}
|
||||
/*如果出现多个#符号的订阅按非法订阅处理*/
|
||||
if (StringUtils.countOccurrencesOf(topicName, "#") > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (topicName.contains("+")) {
|
||||
/*如果+符号和/+字符串出现的次数不等的情况按非法订阅处理*/
|
||||
if (StringUtils.countOccurrencesOf(topicName, "+") != StringUtils.countOccurrencesOf(topicName, "/+")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断topic与topicFilter是否匹配,topic与topicFilter需要符合协议规范
|
||||
*
|
||||
* @param topic: 主题
|
||||
* @param topicFilter: 主题过滤器
|
||||
* @return boolean
|
||||
* @author ZhangJun
|
||||
* @date 23:57 2021/2/27
|
||||
*/
|
||||
public static boolean matchTopic(String topic, String topicFilter) {
|
||||
if (topic.contains("+") || topic.contains("#")) {
|
||||
|
||||
String[] topicSpilts = topic.split("/");
|
||||
String[] filterSpilts = topicFilter.split("/");
|
||||
|
||||
if (!topic.contains("#") && topicSpilts.length < filterSpilts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String level;
|
||||
for (int i = 0; i < topicSpilts.length; i++) {
|
||||
level = topicSpilts[i];
|
||||
if (!level.equals(filterSpilts[i]) && !level.equals("+") && !level.equals("#")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return topic.equals(topicFilter);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定topic搜索所有订阅的topic
|
||||
* 指定的topic没有通配符,但是订阅的时候可能会存在通配符,所以有个查找的过程
|
||||
*
|
||||
* @param topic 主题
|
||||
* @return 返回的所有主题
|
||||
*/
|
||||
public static List<String> searchTopic(String topic) {
|
||||
try {
|
||||
List<String> topicList = new ArrayList<>();
|
||||
topicList.add(topic);
|
||||
/*先处理#通配符*/
|
||||
String[] filterDup = topic.split("/");
|
||||
int[] source = new int[filterDup.length];
|
||||
String itemTopic = "";
|
||||
for (int i = 0; i < filterDup.length; i++) {
|
||||
String item = itemTopic.concat("#");
|
||||
topicList.add(item);
|
||||
itemTopic = itemTopic.concat(filterDup[i]).concat("/");
|
||||
source[i] = i;
|
||||
continue;
|
||||
}
|
||||
/*处理+通配符*/
|
||||
Map<List<Integer>, Boolean> map = TopicsUtils.handle(source);
|
||||
for (List<Integer> key : map.keySet()) {
|
||||
String[] arr = CollectionUtils.copy(filterDup);
|
||||
for (Integer index : key) {
|
||||
arr[index] = "+";
|
||||
}
|
||||
String newTopic = CollectionUtils.concat(arr, "/");
|
||||
topicList.add(newTopic);
|
||||
}
|
||||
return topicList;
|
||||
} catch (Exception e) {
|
||||
log.error("=>查询topic异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Map<List<Integer>, Boolean> handle(int[] src) {
|
||||
int nCnt = src.length;
|
||||
int nBit = (0xFFFFFFFF >>> (32 - nCnt));
|
||||
Map<List<Integer>, Boolean> map = new HashMap<>();
|
||||
for (int i = 1; i <= nBit; i++) {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
for (int j = 0; j < nCnt; j++) {
|
||||
if ((i << (31 - j)) >> 31 == -1) {
|
||||
list.add(j);
|
||||
}
|
||||
}
|
||||
map.put(list, true);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建场景变量上报主题
|
||||
* @param sceneModelId 场景id
|
||||
* @param: sceneModelDeviceId 场景来源id
|
||||
* @return java.lang.String
|
||||
*/
|
||||
public static String buildSceneReportTopic(Long sceneModelId, Long sceneModelDeviceId) {
|
||||
return "/" + sceneModelId + "/" + sceneModelDeviceId + "/scene/report";
|
||||
}
|
||||
|
||||
public static String buildRuleEngineTopic(String requestId) {
|
||||
return "/" + requestId + "/ruleengine/test";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,958 @@
|
||||
package com.fastbee.common.utils.gateway.protocol;
|
||||
|
||||
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public class ByteUtils {
|
||||
|
||||
|
||||
// public static Payload resolvePayload(byte[] content, short start, ModbusCode code) {
|
||||
// Payload payload;
|
||||
// switch (code) {
|
||||
// case Read01:
|
||||
// case Read02:
|
||||
// payload = new RealCoilPayload(start, content); break;
|
||||
// case Read03:
|
||||
// case Read04:
|
||||
// payload = new ReadPayload(content, start); break;
|
||||
// default:
|
||||
// payload = WritePayload.getInstance();
|
||||
// }
|
||||
//
|
||||
// return payload;
|
||||
// }
|
||||
|
||||
public static Write10Build write10Build(Object... args) {
|
||||
int num = 0; List<byte[]> bytes = new ArrayList<>();
|
||||
for(Object arg : args) {
|
||||
if(arg instanceof Integer) {
|
||||
num += 2;
|
||||
bytes.add(getBytes((Integer) arg));
|
||||
} else if(arg instanceof Long) {
|
||||
num += 4;
|
||||
bytes.add(getBytes((Long) arg));
|
||||
} else if(arg instanceof Float) {
|
||||
num += 2;
|
||||
bytes.add(getBytes((Float) arg));
|
||||
} else if(arg instanceof Double) {
|
||||
num += 4;
|
||||
bytes.add(getBytes((Double) arg));
|
||||
} else if(arg instanceof Short) {
|
||||
num += 1;
|
||||
bytes.add(getBytesOfReverse((Short) arg));
|
||||
} else if(arg instanceof Byte) {
|
||||
num += 1;
|
||||
bytes.add(new byte[]{0x00, (byte) arg});
|
||||
} else if(arg instanceof String) {
|
||||
byte[] bytes1 = arg.toString().getBytes(StandardCharsets.UTF_8);
|
||||
if(bytes1.length % 2 != 0) {
|
||||
num += bytes1.length / 2 + 1;
|
||||
byte[] addMessage = new byte[bytes1.length + 1];
|
||||
addBytes(addMessage, bytes1, 0);
|
||||
bytes.add(addMessage);
|
||||
} else {
|
||||
num += bytes1.length / 2;
|
||||
bytes.add(bytes1);
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException("不支持的数据类型");
|
||||
}
|
||||
}
|
||||
|
||||
Integer length = bytes.stream().map(item -> item.length).reduce((a, b) -> a + b).get();
|
||||
byte[] write = new byte[length];
|
||||
|
||||
int index = 0;
|
||||
for(int i=0; i<bytes.size(); i++) {
|
||||
byte[] values = bytes.get(i);
|
||||
addBytes(write, values, index);
|
||||
index += values.length;
|
||||
}
|
||||
|
||||
return new Write10Build(num, write);
|
||||
}
|
||||
|
||||
public static class Write10Build {
|
||||
public int num;
|
||||
public byte[] message;
|
||||
|
||||
public Write10Build(int num, byte[] message) {
|
||||
this.num = num;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] create(byte b){
|
||||
return new byte[]{b};
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节转成字节数组
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static byte[] getBytes(byte data) {
|
||||
return new byte[] {data};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将short数据类型转化成Byte数组
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param data short值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytes(short data) {
|
||||
byte[] bytes = new byte[2];
|
||||
bytes[0] = (byte) (data & 0xff);
|
||||
bytes[1] = (byte) ((data & 0xff00) >> 8);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将short数据类型转化成Byte数组
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param data short值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytesOfReverse(short data) {
|
||||
byte[] bytes = new byte[2];
|
||||
bytes[1] = (byte) (data & 0xff);
|
||||
bytes[0] = (byte) ((data & 0xff00) >> 8);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param bytes
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
public static short bytesToShort(byte[] bytes, int offset) {
|
||||
return (short) ((0xff & bytes[0 + offset]) | (0xff00 & (bytes[1 + offset] << 8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成short数据
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param bytes 字节数组
|
||||
* @return short值
|
||||
*/
|
||||
public static short bytesToShortOfReverse(byte[] bytes) {
|
||||
return ByteUtils.bytesToShortOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成short数据,采用倒序的表达方式
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return short值
|
||||
*/
|
||||
public static short bytesToShortOfReverse(byte[] bytes, int offset) {
|
||||
return (short) ((0xff & bytes[1 + offset]) | (0xff00 & (bytes[0 + offset] << 8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数据类型转化成Byte数组
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param data int值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytes(int data) {
|
||||
byte[] bytes = new byte[4];
|
||||
bytes[0] = (byte) (data & 0xff);
|
||||
bytes[1] = (byte) ((data >> 8) & 0xff);
|
||||
bytes[2] = (byte) ((data >> 16) & 0xff);
|
||||
bytes[3] = (byte) ((data >> 24) & 0xff);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数据类型转化成Byte数组 倒序
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param data int值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytesOfReverse(int data) {
|
||||
byte[] src = new byte[4];
|
||||
src[0] = (byte) ((data >> 24) & 0xFF);
|
||||
src[1] = (byte) ((data >> 16) & 0xFF);
|
||||
src[2] = (byte) ((data >> 8) & 0xFF);
|
||||
src[3] = (byte) (data & 0xFF);
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将long数据类型转化成Byte数组
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param data long值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytes(long data) {
|
||||
byte[] bytes = new byte[8];
|
||||
bytes[0] = (byte) (data & 0xff);
|
||||
bytes[1] = (byte) ((data >> 8) & 0xff);
|
||||
bytes[2] = (byte) ((data >> 16) & 0xff);
|
||||
bytes[3] = (byte) ((data >> 24) & 0xff);
|
||||
bytes[4] = (byte) ((data >> 32) & 0xff);
|
||||
bytes[5] = (byte) ((data >> 40) & 0xff);
|
||||
bytes[6] = (byte) ((data >> 48) & 0xff);
|
||||
bytes[7] = (byte) ((data >> 56) & 0xff);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将long数据类型转化成Byte数组 倒序
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param data long值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytesOfReverse(long data) {
|
||||
byte[] bytes = new byte[8];
|
||||
bytes[7] = (byte) (data & 0xff);
|
||||
bytes[6] = (byte) ((data >> 8) & 0xff);
|
||||
bytes[5] = (byte) ((data >> 16) & 0xff);
|
||||
bytes[4] = (byte) ((data >> 24) & 0xff);
|
||||
bytes[3] = (byte) ((data >> 32) & 0xff);
|
||||
bytes[2] = (byte) ((data >> 40) & 0xff);
|
||||
bytes[1] = (byte) ((data >> 48) & 0xff);
|
||||
bytes[0] = (byte) ((data >> 56) & 0xff);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将float数据类型转化成Byte数组
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param data float值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytes(float data) {
|
||||
int intBits = Float.floatToIntBits(data);
|
||||
return getBytes(intBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将float数据类型转化成Byte数组 倒序
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param data float值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytesOfReverse(float data) {
|
||||
int intBits = Float.floatToIntBits(data);
|
||||
return getBytesOfReverse(intBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将double数据类型转化成Byte数组
|
||||
* @see ByteOrder#LITTLE_ENDIAN
|
||||
* @param data double值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytes(double data) {
|
||||
long intBits = Double.doubleToLongBits(data);
|
||||
return getBytes(intBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将double数据类型转化成Byte数组 倒序
|
||||
* @see ByteOrder#BIG_ENDIAN
|
||||
* @param data double值
|
||||
* @return byte[]数组
|
||||
*/
|
||||
public static byte[] getBytesOfReverse(double data) {
|
||||
long intBits = Double.doubleToLongBits(data);
|
||||
return getBytesOfReverse(intBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转字节数组(UTF-8)
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static byte[] getBytes(String data) {
|
||||
return data.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换成byte[]数组
|
||||
* @param data 字符串值
|
||||
* @param charsetName 编码方式
|
||||
* @return 字节数组
|
||||
*/
|
||||
public static byte[] getBytes(String data, String charsetName) {
|
||||
Charset charset = Charset.forName(charsetName);
|
||||
return data.getBytes(charset);
|
||||
}
|
||||
|
||||
/*
|
||||
* 把16进制字符串转换成字节数组
|
||||
* @param hex
|
||||
* @return
|
||||
*/
|
||||
public static byte[] hexToByte(String hexStr) {
|
||||
if(StringUtils.isBlank(hexStr)) {
|
||||
return null;
|
||||
}
|
||||
if(hexStr.length()%2 != 0) {//长度为单数
|
||||
hexStr = "0" + hexStr;//前面补0
|
||||
}
|
||||
|
||||
char[] chars = hexStr.toCharArray();
|
||||
int len = chars.length/2;
|
||||
byte[] bytes = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
int x = i*2;
|
||||
bytes[i] = (byte)Integer.parseInt(String.valueOf(new char[]{chars[x], chars[x+1]}), 16);
|
||||
}
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
public static byte getByte(byte[] src, int offset) {
|
||||
return src[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转16进制
|
||||
* @param bArray
|
||||
* @return
|
||||
*/
|
||||
public static final String bytesToHex(byte[] bArray) {
|
||||
StringBuffer sb = new StringBuffer(bArray.length);
|
||||
String sTemp;
|
||||
for (int i = 0; i < bArray.length; i++) {
|
||||
sTemp = Integer.toHexString(0xFF & bArray[i]);
|
||||
if (sTemp.length() < 2) sb.append(0);
|
||||
sb.append(sTemp.toUpperCase());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转16进制且格式化十六进制
|
||||
* @param bArray
|
||||
* @return
|
||||
*/
|
||||
public static final String bytesToHexByFormat(byte[] bArray) {
|
||||
StringBuffer sb = new StringBuffer(bArray.length);
|
||||
String sTemp;
|
||||
for (int i = 0; i < bArray.length; i++) {
|
||||
sTemp = Integer.toHexString(0xFF & bArray[i]);
|
||||
if (sTemp.length() < 2) sb.append(0);
|
||||
sb.append(sTemp.toUpperCase()).append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转16进制
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static final String bytesToHex(byte[] src, int offset, int length) {
|
||||
byte[] bArray = ArrayUtils.subarray(src, offset, offset + length);
|
||||
return bytesToHex(bArray);
|
||||
}
|
||||
|
||||
public static final String byteToHex(byte value) {
|
||||
String s = Integer.toHexString(0xff & value);
|
||||
if(s.length() == 1) return "0"+s;
|
||||
return s;
|
||||
}
|
||||
|
||||
public static final String shortToHex(short value) {
|
||||
String s = Integer.toHexString(value);
|
||||
switch (s.length()) {
|
||||
case 1: return "000" + s;
|
||||
case 2: return "00" + s;
|
||||
case 3: return "0" + s;
|
||||
default: return s;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String intToHex(int value) {
|
||||
StringBuilder s = new StringBuilder(Integer.toHexString(value));
|
||||
String v1 = s.toString().replace("f","");
|
||||
if (v1.length() <4){
|
||||
for (int i = 0; i < 4 - v1.length(); i++) {
|
||||
s.insert(0, "0");
|
||||
}
|
||||
return s.toString().replace("f","");
|
||||
}
|
||||
else return s.toString().replace("f","");
|
||||
}
|
||||
|
||||
public static final String hexTo8Bit(int value,int index){
|
||||
String s = Integer.toBinaryString(value);
|
||||
StringBuilder result = new StringBuilder(s);
|
||||
if (s.length() < index){
|
||||
for (int i = 0; i < index - s.length(); i++) {
|
||||
result.insert(0,"0");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @函数功能: BCD码转为10进制串(阿拉伯数据)
|
||||
* @输入参数: BCD码
|
||||
* @输出结果: 10进制串
|
||||
*/
|
||||
public static String bcdToStr(byte[] bytes){
|
||||
StringBuffer temp=new StringBuffer(bytes.length*2);
|
||||
|
||||
for(int i=0;i<bytes.length;i++){
|
||||
temp.append((byte)((bytes[i] & 0xf0)>>>4));
|
||||
temp.append((byte)(bytes[i] & 0x0f));
|
||||
}
|
||||
|
||||
return temp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param src 原报文
|
||||
* @param offset 起始位置
|
||||
* @param length 长度
|
||||
* @return
|
||||
*/
|
||||
public static String bcdToStr(byte[] src, int offset, int length){
|
||||
byte[] bArray = ArrayUtils.subarray(src, offset, offset + length);
|
||||
return bcdToStr(bArray);
|
||||
}
|
||||
|
||||
public static byte[] str2Bcd(String asc) {
|
||||
int len = asc.length();
|
||||
int mod = len % 2;
|
||||
|
||||
if (mod != 0) {
|
||||
asc = "0" + asc;
|
||||
len = asc.length();
|
||||
}
|
||||
|
||||
byte abt[];
|
||||
if (len >= 2) {
|
||||
len = len / 2;
|
||||
}
|
||||
|
||||
byte bbt[] = new byte[len];
|
||||
abt = asc.getBytes();
|
||||
int j, k;
|
||||
|
||||
for (int p = 0; p < asc.length()/2; p++) {
|
||||
if ( (abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
|
||||
j = abt[2 * p] - '0';
|
||||
} else if ( (abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
|
||||
j = abt[2 * p] - 'a' + 0x0a;
|
||||
} else {
|
||||
j = abt[2 * p] - 'A' + 0x0a;
|
||||
}
|
||||
|
||||
if ( (abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
|
||||
k = abt[2 * p + 1] - '0';
|
||||
} else if ( (abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
|
||||
k = abt[2 * p + 1] - 'a' + 0x0a;
|
||||
}else {
|
||||
k = abt[2 * p + 1] - 'A' + 0x0a;
|
||||
}
|
||||
|
||||
int a = (j << 4) + k;
|
||||
byte b = (byte) a;
|
||||
bbt[p] = b;
|
||||
}
|
||||
return bbt;
|
||||
}
|
||||
|
||||
private static byte toByte(char c) {
|
||||
return (byte) c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成 ushort 数据
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return short值
|
||||
*/
|
||||
public static int bytesToUShort(byte[] bytes, int offset) {
|
||||
return ((0xff & bytes[0 + offset]) | (0xff00 & (bytes[1 + offset] << 8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成 ushort 数据,采用倒序的方式
|
||||
* @param bytes 字节数组
|
||||
* @return short值
|
||||
*/
|
||||
public static int bytesToUShortOfReverse(byte[] bytes) {
|
||||
return ByteUtils.bytesToShortOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成 ushort 数据,采用倒序的方式
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return short值
|
||||
*/
|
||||
public static int bytesToUShortOfReverse(byte[] bytes, int offset) {
|
||||
return ((0xff & bytes[1 + offset]) | (0xff00 & (bytes[0 + offset] << 8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和intToBytes配套使用
|
||||
*
|
||||
* @param src
|
||||
* byte数组
|
||||
* @param offset
|
||||
* 从数组的第offset位开始
|
||||
* @return int数值
|
||||
*/
|
||||
public static int bytesToInt(byte[] src, int offset) {
|
||||
return ((src[offset] & 0xFF) | ((src[offset + 1] & 0xFF) << 8)
|
||||
| ((src[offset + 2] & 0xFF) << 16) | ((src[offset + 3] & 0xFF) << 24));
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和intToBytes配套使用
|
||||
* @param src byte数组
|
||||
* @return int数值
|
||||
*/
|
||||
public static int bytesToInt(byte[] src) {
|
||||
return ByteUtils.bytesToInt(src, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成int数据,采用倒序的方式
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return int值
|
||||
*/
|
||||
public static int bytesToIntOfReverse(byte[] bytes, int offset) {
|
||||
return (0xff & bytes[3 + offset]) |
|
||||
(0xff00 & (bytes[2 + offset] << 8)) |
|
||||
(0xff0000 & (bytes[1 + offset] << 16)) |
|
||||
(0xff000000 & (bytes[0 + offset] << 24));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成int数据,采用倒序的方式
|
||||
* @param bytes 字节数组
|
||||
* @return int值
|
||||
*/
|
||||
public static int bytesToIntOfReverse(byte[] bytes) {
|
||||
return ByteUtils.bytesToIntOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成uint数据
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return int值
|
||||
*/
|
||||
public static long bytesToUInt(byte[] bytes, int offset) {
|
||||
int value = bytesToInt(bytes, offset);
|
||||
if (value >= 0) return value;
|
||||
return 65536L * 65536L + value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成uint数据
|
||||
* @param bytes 字节数组
|
||||
* @return int值
|
||||
*/
|
||||
public static long bytesToUInt(byte[] bytes) {
|
||||
return ByteUtils.bytesToUInt(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成uint数据
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return int值
|
||||
*/
|
||||
public static long bytesToUIntOfReverse(byte[] bytes, int offset) {
|
||||
int value = bytesToIntOfReverse(bytes, offset);
|
||||
if (value >= 0) return value;
|
||||
return 65536L * 65536L + value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成uint数据 倒序
|
||||
* @param bytes 字节数组
|
||||
* @return int值
|
||||
*/
|
||||
public static long bytesToUIntOfReverse(byte[] bytes) {
|
||||
return ByteUtils.bytesToUIntOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成float数据
|
||||
* @param bytes 字节数组
|
||||
* @return float值
|
||||
*/
|
||||
public static float bytesToFloat(byte[] bytes) {
|
||||
return Float.intBitsToFloat(bytesToInt(bytes, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成float数据
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return float值
|
||||
*/
|
||||
public static float bytesToFloat(byte[] bytes, int offset) {
|
||||
return Float.intBitsToFloat(bytesToInt(bytes,offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成float数据
|
||||
* @param bytes 字节数组
|
||||
* @return float值
|
||||
*/
|
||||
public static float bytesToFloatOfReverse(byte[] bytes) {
|
||||
return bytesToFloatOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成float数据
|
||||
* @param bytes 字节数组
|
||||
* @param offset 偏移量
|
||||
* @return float值
|
||||
*/
|
||||
public static float bytesToFloatOfReverse(byte[] bytes, int offset) {
|
||||
return Float.intBitsToFloat(bytesToIntOfReverse(bytes, offset));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* byte数组中取double数值
|
||||
* @param src byte数组
|
||||
* @return double数值
|
||||
*/
|
||||
public static double bytesToDouble(byte[] src) {
|
||||
return Double.longBitsToDouble(bytesToLong(src));
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取double数值
|
||||
* @param src byte数组
|
||||
* @param offset 从数组的第offset位开始
|
||||
* @return double数值
|
||||
*/
|
||||
public static double bytesToDouble(byte[] src, int offset) {
|
||||
return Double.longBitsToDouble(bytesToLong(src, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取double数值
|
||||
* @param src byte数组
|
||||
* @return double数值
|
||||
*/
|
||||
public static double bytesToDoubleOfReverse(byte[] src) {
|
||||
return bytesToDoubleOfReverse(src, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组中取double数值
|
||||
* @param src byte数组
|
||||
* @param offset 从数组的第offset位开始
|
||||
* @return double数值
|
||||
*/
|
||||
public static double bytesToDoubleOfReverse(byte[] src, int offset) {
|
||||
return Double.longBitsToDouble(bytesToLongOfReverse(src, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉字节数组尾数为零的字节,并将其转成字符串
|
||||
* @param src
|
||||
* @param charset
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToString(byte[] src, Charset charset){
|
||||
int search = Arrays.binarySearch(src, (byte) 0);
|
||||
return new String(Arrays.copyOf(src, search), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉字节数组尾数为零的字节,并将其转成字符串
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToString(byte[] src){
|
||||
return new String(wipeLastZero(src));
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉字节数组尾数为零的字节,并将其转成字符串
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToString(byte[] src, int startIndex, int endIndex){
|
||||
return new String(wipeLastZero(subBytes(src, startIndex, endIndex)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉字节数组尾数为零的字节,并将其转成字符串
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToString(byte[] src, int startIndex, int endIndex, Charset charset){
|
||||
return new String(wipeLastZero(subBytes(src, startIndex, endIndex)), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte[]数组的数据进行翻转
|
||||
* @param reverse 等待反转的字符串
|
||||
*/
|
||||
public static void bytesReverse(byte[] reverse) {
|
||||
if (reverse != null) {
|
||||
byte tmp = 0;
|
||||
for (int i = 0; i < reverse.length / 2; i++) {
|
||||
tmp = reverse[i];
|
||||
reverse[i] = reverse[reverse.length - 1 - i];
|
||||
reverse[reverse.length - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除包含0的字节
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
private static byte[] wipeLastZero(byte[] src){
|
||||
int index = 0;
|
||||
for(int i=0; i<src.length; i++){
|
||||
if(src[i] == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return subBytes(src, 0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节转为long, 偏移位从0开始
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static long bytesToLong(byte[] bytes) {
|
||||
return ByteUtils.bytesToLong(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节转为long
|
||||
* @param bytes
|
||||
* @param offset 偏移位置
|
||||
* @return
|
||||
*/
|
||||
public static long bytesToLong(byte[] bytes, int offset) {
|
||||
return (0xffL & (long) bytes[0 + offset]) |
|
||||
(0xff00L & ((long) bytes[1 + offset] << 8)) |
|
||||
(0xff0000L & ((long) bytes[2 + offset] << 16)) |
|
||||
(0xff000000L & ((long) bytes[3 + offset] << 24)) |
|
||||
(0xff00000000L & ((long) bytes[4 + offset] << 32)) |
|
||||
(0xff0000000000L & ((long) bytes[5 + offset] << 40)) |
|
||||
(0xff000000000000L & ((long) bytes[6 + offset] << 48)) |
|
||||
(0xff00000000000000L & ((long) bytes[7 + offset] << 56));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成long数据 倒序
|
||||
* @param bytes 字节数组
|
||||
* @return long值
|
||||
*/
|
||||
public static long bytesToLongOfReverse(byte[] bytes) {
|
||||
return ByteUtils.bytesToLongOfReverse(bytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换成long数据 倒序
|
||||
* @param bytes 字节数组
|
||||
* @param offset 起始位置
|
||||
* @return long值
|
||||
*/
|
||||
public static long bytesToLongOfReverse(byte[] bytes, int offset) {
|
||||
return (0xffL & (long) bytes[7 + offset]) |
|
||||
(0xff00L & ((long) bytes[6 + offset] << 8)) |
|
||||
(0xff0000L & ((long) bytes[5 + offset] << 16)) |
|
||||
(0xff000000L & ((long) bytes[4 + offset] << 24)) |
|
||||
(0xff00000000L & ((long) bytes[3 + offset] << 32)) |
|
||||
(0xff0000000000L & ((long) bytes[2 + offset] << 40)) |
|
||||
(0xff000000000000L & ((long) bytes[1 + offset] << 48)) |
|
||||
(0xff00000000000000L & ((long) bytes[0 + offset] << 56));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符数组 切割
|
||||
* @param bytes 字节数组
|
||||
* @param beginIndex 开始索引
|
||||
* @param endIndex 结束索引
|
||||
* @return
|
||||
*/
|
||||
public static byte[] subBytes(byte[] bytes, int beginIndex, int endIndex) {
|
||||
byte[] result = new byte[endIndex - beginIndex];
|
||||
System.arraycopy(bytes, beginIndex, result, 0, result.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 子字节 从开始位置{beginIndex} 到结束
|
||||
* @param bytes
|
||||
* @param beginIndex
|
||||
* @return
|
||||
*/
|
||||
public static byte[] subBytes(byte[] bytes, int beginIndex) {
|
||||
return ByteUtils.subBytes(bytes, beginIndex, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将截取数组指定数据,并将其转为字符串
|
||||
* @param bytes
|
||||
* @param beginIndex
|
||||
* @param endIndex
|
||||
* @return
|
||||
*/
|
||||
public static String subBytesToString(byte[] bytes, int beginIndex, int endIndex){
|
||||
byte[] subBytes = subBytes(bytes, beginIndex, endIndex);
|
||||
return bytesToString(subBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组添加整个target数组
|
||||
*
|
||||
* 源字节数组
|
||||
* @param targetBytes
|
||||
* 添加对象数组
|
||||
* @param beginIndex
|
||||
* 开始下标
|
||||
* @return
|
||||
*/
|
||||
public static byte[] addBytes(byte[] sourceBytes, byte[] targetBytes, int beginIndex) {
|
||||
if (targetBytes == null) {
|
||||
return sourceBytes;
|
||||
}
|
||||
int targetSize = targetBytes.length;
|
||||
if (sourceBytes == null) {
|
||||
beginIndex = 0;
|
||||
sourceBytes = new byte[targetSize];
|
||||
}
|
||||
int sourceSize = sourceBytes.length;
|
||||
if (sourceSize - beginIndex < targetSize) {
|
||||
return sourceBytes;
|
||||
} else {
|
||||
for (int i = 0; i < targetSize; i++) {
|
||||
sourceBytes[beginIndex + i] = targetBytes[i];
|
||||
}
|
||||
}
|
||||
return sourceBytes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个uuid对象转化成byte[]
|
||||
* @param uuid uuid对象
|
||||
* @return 字节数组
|
||||
*/
|
||||
public static byte[] UUID2Byte(UUID uuid) {
|
||||
ByteArrayOutputStream ba = new ByteArrayOutputStream(16);
|
||||
DataOutputStream da = new DataOutputStream(ba);
|
||||
try {
|
||||
da.writeLong(uuid.getMostSignificantBits());
|
||||
da.writeLong(uuid.getLeastSignificantBits());
|
||||
ba.close();
|
||||
da.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
byte[] buffer = ba.toByteArray();
|
||||
// 进行错位
|
||||
byte temp=buffer[0];
|
||||
buffer[0] = buffer[3];
|
||||
buffer[3] =temp;
|
||||
temp=buffer[1];
|
||||
buffer[1]=buffer[2];
|
||||
buffer[2]=temp;
|
||||
|
||||
temp = buffer[4];
|
||||
buffer[4]=buffer[5];
|
||||
buffer[5] =temp;
|
||||
|
||||
temp = buffer[6];
|
||||
buffer[6]=buffer[7];
|
||||
buffer[7] =temp;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字节的指定位值 0 or 1
|
||||
* @param value
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public static byte bitAtByte(byte value, int index) {
|
||||
return (byte) ((value & (1 << index)) > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*将bool数组转换到byte数组<br />
|
||||
* @param array bool数组
|
||||
* @return 字节数组
|
||||
*/
|
||||
public static byte[] boolArrayToByte(boolean[] array) {
|
||||
if (array == null) return null;
|
||||
|
||||
int length = array.length % 8 == 0 ? array.length / 8 : array.length / 8 + 1;
|
||||
byte[] buffer = new byte[length];
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i]) {
|
||||
buffer[i / 8] += (1 << i % 8);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Integer cutMessageHexTo(byte[] source, int startIndex, int endIndex){
|
||||
byte[] subarray = ArrayUtils.subarray(source, startIndex, endIndex);
|
||||
String s = bytesToHexString(subarray);
|
||||
return Integer.parseInt(s,16);
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组转换炒年糕十六进制字符串
|
||||
*
|
||||
* @param bArray byte数组
|
||||
* @return hex字符串
|
||||
*/
|
||||
public static String bytesToHexString(byte[] bArray) {
|
||||
StringBuilder sb = new StringBuilder(bArray.length);
|
||||
for (int i = 0; i < bArray.length; i++) {
|
||||
String hexStr = Integer.toHexString(0xFF & bArray[i]);
|
||||
if (hexStr.length() < 2) {
|
||||
sb.append(0);
|
||||
}
|
||||
sb.append(hexStr.toUpperCase());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fastbee.common.utils.gateway.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2022/9/15 14:44
|
||||
*/
|
||||
public class NettyUtils {
|
||||
|
||||
/**
|
||||
* ByteBuf转 byte[]
|
||||
* @param buf buffer
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] readBytesFromByteBuf(ByteBuf buf){
|
||||
return ByteBufUtil.getBytes(buf);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.fastbee.common.utils.html;
|
||||
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 转义和反转义工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class EscapeUtil
|
||||
{
|
||||
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
|
||||
|
||||
private static final char[][] TEXT = new char[64][];
|
||||
|
||||
static
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
TEXT[i] = new char[] { (char) i };
|
||||
}
|
||||
|
||||
// special HTML characters
|
||||
TEXT['\''] = "'".toCharArray(); // 单引号
|
||||
TEXT['"'] = """.toCharArray(); // 双引号
|
||||
TEXT['&'] = "&".toCharArray(); // &符
|
||||
TEXT['<'] = "<".toCharArray(); // 小于号
|
||||
TEXT['>'] = ">".toCharArray(); // 大于号
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义文本中的HTML字符为安全的字符
|
||||
*
|
||||
* @param text 被转义的文本
|
||||
* @return 转义后的文本
|
||||
*/
|
||||
public static String escape(String text)
|
||||
{
|
||||
return encode(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原被转义的HTML特殊字符
|
||||
*
|
||||
* @param content 包含转义符的HTML内容
|
||||
* @return 转换后的字符串
|
||||
*/
|
||||
public static String unescape(String content)
|
||||
{
|
||||
return decode(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有HTML标签,但是不删除标签内的内容
|
||||
*
|
||||
* @param content 文本
|
||||
* @return 清除标签后的文本
|
||||
*/
|
||||
public static String clean(String content)
|
||||
{
|
||||
return new HTMLFilter().filter(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape编码
|
||||
*
|
||||
* @param text 被编码的文本
|
||||
* @return 编码后的字符
|
||||
*/
|
||||
private static String encode(String text)
|
||||
{
|
||||
if (StringUtils.isEmpty(text))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder tmp = new StringBuilder(text.length() * 6);
|
||||
char c;
|
||||
for (int i = 0; i < text.length(); i++)
|
||||
{
|
||||
c = text.charAt(i);
|
||||
if (c < 256)
|
||||
{
|
||||
tmp.append("%");
|
||||
if (c < 16)
|
||||
{
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp.append("%u");
|
||||
if (c <= 0xfff)
|
||||
{
|
||||
// issue#I49JU8@Gitee
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape解码
|
||||
*
|
||||
* @param content 被转义的内容
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decode(String content)
|
||||
{
|
||||
if (StringUtils.isEmpty(content))
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
StringBuilder tmp = new StringBuilder(content.length());
|
||||
int lastPos = 0, pos = 0;
|
||||
char ch;
|
||||
while (lastPos < content.length())
|
||||
{
|
||||
pos = content.indexOf("%", lastPos);
|
||||
if (pos == lastPos)
|
||||
{
|
||||
if (content.charAt(pos + 1) == 'u')
|
||||
{
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos == -1)
|
||||
{
|
||||
tmp.append(content.substring(lastPos));
|
||||
lastPos = content.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp.append(content.substring(lastPos, pos));
|
||||
lastPos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
String html = "<script>alert(1);</script>";
|
||||
String escape = EscapeUtil.escape(html);
|
||||
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
|
||||
// String html = "<123";
|
||||
// String html = "123>";
|
||||
System.out.println("clean: " + EscapeUtil.clean(html));
|
||||
System.out.println("escape: " + escape);
|
||||
System.out.println("unescape: " + EscapeUtil.unescape(escape));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
package com.fastbee.common.utils.html;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* HTML过滤器,用于去除XSS漏洞隐患。
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public final class HTMLFilter
|
||||
{
|
||||
/**
|
||||
* regex flag union representing /si modifiers in php
|
||||
**/
|
||||
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
|
||||
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
|
||||
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
|
||||
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_AMP = Pattern.compile("&");
|
||||
private static final Pattern P_QUOTE = Pattern.compile("\"");
|
||||
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||
|
||||
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* set of allowed html elements, along with allowed attributes for each element
|
||||
**/
|
||||
private final Map<String, List<String>> vAllowed;
|
||||
/**
|
||||
* counts of open tags for each (allowable) html element
|
||||
**/
|
||||
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||
|
||||
/**
|
||||
* html elements which must always be self-closing (e.g. "<img />")
|
||||
**/
|
||||
private final String[] vSelfClosingTags;
|
||||
/**
|
||||
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||
**/
|
||||
private final String[] vNeedClosingTags;
|
||||
/**
|
||||
* set of disallowed html elements
|
||||
**/
|
||||
private final String[] vDisallowed;
|
||||
/**
|
||||
* attributes which should be checked for valid protocols
|
||||
**/
|
||||
private final String[] vProtocolAtts;
|
||||
/**
|
||||
* allowed protocols
|
||||
**/
|
||||
private final String[] vAllowedProtocols;
|
||||
/**
|
||||
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
|
||||
**/
|
||||
private final String[] vRemoveBlanks;
|
||||
/**
|
||||
* entities allowed within html markup
|
||||
**/
|
||||
private final String[] vAllowedEntities;
|
||||
/**
|
||||
* flag determining whether comments are allowed in input String.
|
||||
*/
|
||||
private final boolean stripComment;
|
||||
private final boolean encodeQuotes;
|
||||
/**
|
||||
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
|
||||
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
|
||||
*/
|
||||
private final boolean alwaysMakeTags;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public HTMLFilter()
|
||||
{
|
||||
vAllowed = new HashMap<>();
|
||||
|
||||
final ArrayList<String> a_atts = new ArrayList<>();
|
||||
a_atts.add("href");
|
||||
a_atts.add("target");
|
||||
vAllowed.put("a", a_atts);
|
||||
|
||||
final ArrayList<String> img_atts = new ArrayList<>();
|
||||
img_atts.add("src");
|
||||
img_atts.add("width");
|
||||
img_atts.add("height");
|
||||
img_atts.add("alt");
|
||||
vAllowed.put("img", img_atts);
|
||||
|
||||
final ArrayList<String> no_atts = new ArrayList<>();
|
||||
vAllowed.put("b", no_atts);
|
||||
vAllowed.put("strong", no_atts);
|
||||
vAllowed.put("i", no_atts);
|
||||
vAllowed.put("em", no_atts);
|
||||
|
||||
vSelfClosingTags = new String[] { "img" };
|
||||
vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
|
||||
vDisallowed = new String[] {};
|
||||
vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
|
||||
vProtocolAtts = new String[] { "src", "href" };
|
||||
vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
|
||||
vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
|
||||
stripComment = true;
|
||||
encodeQuotes = true;
|
||||
alwaysMakeTags = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map-parameter configurable constructor.
|
||||
*
|
||||
* @param conf map containing configuration. keys match field names.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public HTMLFilter(final Map<String, Object> conf)
|
||||
{
|
||||
|
||||
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||
|
||||
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
vTagCounts.clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// my versions of some PHP library functions
|
||||
public static String chr(final int decimal)
|
||||
{
|
||||
return String.valueOf((char) decimal);
|
||||
}
|
||||
|
||||
public static String htmlSpecialChars(final String s)
|
||||
{
|
||||
String result = s;
|
||||
result = regexReplace(P_AMP, "&", result);
|
||||
result = regexReplace(P_QUOTE, """, result);
|
||||
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||
result = regexReplace(P_RIGHT_ARROW, ">", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* given a user submitted input String, filter out any invalid or restricted html.
|
||||
*
|
||||
* @param input text (i.e. submitted by a user) than may contain html
|
||||
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||
*/
|
||||
public String filter(final String input)
|
||||
{
|
||||
reset();
|
||||
String s = input;
|
||||
|
||||
s = escapeComments(s);
|
||||
|
||||
s = balanceHTML(s);
|
||||
|
||||
s = checkTags(s);
|
||||
|
||||
s = processRemoveBlanks(s);
|
||||
|
||||
// s = validateEntities(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean isAlwaysMakeTags()
|
||||
{
|
||||
return alwaysMakeTags;
|
||||
}
|
||||
|
||||
public boolean isStripComments()
|
||||
{
|
||||
return stripComment;
|
||||
}
|
||||
|
||||
private String escapeComments(final String s)
|
||||
{
|
||||
final Matcher m = P_COMMENTS.matcher(s);
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
if (m.find())
|
||||
{
|
||||
final String match = m.group(1); // (.*?)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private String balanceHTML(String s)
|
||||
{
|
||||
if (alwaysMakeTags)
|
||||
{
|
||||
//
|
||||
// try and form html
|
||||
//
|
||||
s = regexReplace(P_END_ARROW, "", s);
|
||||
// 不追加结束标签
|
||||
s = regexReplace(P_BODY_TO_END, "<$1>", s);
|
||||
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// escape stray brackets
|
||||
//
|
||||
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||
|
||||
//
|
||||
// the last regexp causes '<>' entities to appear
|
||||
// (we need to do a lookahead assertion so that the last bracket can
|
||||
// be used in the next pass of the regexp)
|
||||
//
|
||||
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String checkTags(String s)
|
||||
{
|
||||
Matcher m = P_TAGS.matcher(s);
|
||||
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
while (m.find())
|
||||
{
|
||||
String replaceStr = m.group(1);
|
||||
replaceStr = processTag(replaceStr);
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
// these get tallied in processTag
|
||||
// (remember to reset before subsequent calls to filter method)
|
||||
final StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||
for (String key : vTagCounts.keySet())
|
||||
{
|
||||
for (int ii = 0; ii < vTagCounts.get(key); ii++)
|
||||
{
|
||||
sBuilder.append("</").append(key).append(">");
|
||||
}
|
||||
}
|
||||
s = sBuilder.toString();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String processRemoveBlanks(final String s)
|
||||
{
|
||||
String result = s;
|
||||
for (String tag : vRemoveBlanks)
|
||||
{
|
||||
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
|
||||
{
|
||||
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
|
||||
if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
|
||||
{
|
||||
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
|
||||
{
|
||||
Matcher m = regex_pattern.matcher(s);
|
||||
return m.replaceAll(replacement);
|
||||
}
|
||||
|
||||
private String processTag(final String s)
|
||||
{
|
||||
// ending tags
|
||||
Matcher m = P_END_TAG.matcher(s);
|
||||
if (m.find())
|
||||
{
|
||||
final String name = m.group(1).toLowerCase();
|
||||
if (allowed(name))
|
||||
{
|
||||
if (!inArray(name, vSelfClosingTags))
|
||||
{
|
||||
if (vTagCounts.containsKey(name))
|
||||
{
|
||||
vTagCounts.put(name, vTagCounts.get(name) - 1);
|
||||
return "</" + name + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// starting tags
|
||||
m = P_START_TAG.matcher(s);
|
||||
if (m.find())
|
||||
{
|
||||
final String name = m.group(1).toLowerCase();
|
||||
final String body = m.group(2);
|
||||
String ending = m.group(3);
|
||||
|
||||
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
||||
if (allowed(name))
|
||||
{
|
||||
final StringBuilder params = new StringBuilder();
|
||||
|
||||
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
|
||||
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
|
||||
final List<String> paramNames = new ArrayList<>();
|
||||
final List<String> paramValues = new ArrayList<>();
|
||||
while (m2.find())
|
||||
{
|
||||
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m2.group(3)); // (.*?)
|
||||
}
|
||||
while (m3.find())
|
||||
{
|
||||
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
||||
}
|
||||
|
||||
String paramName, paramValue;
|
||||
for (int ii = 0; ii < paramNames.size(); ii++)
|
||||
{
|
||||
paramName = paramNames.get(ii).toLowerCase();
|
||||
paramValue = paramValues.get(ii);
|
||||
|
||||
// debug( "paramName='" + paramName + "'" );
|
||||
// debug( "paramValue='" + paramValue + "'" );
|
||||
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||
|
||||
if (allowedAttribute(name, paramName))
|
||||
{
|
||||
if (inArray(paramName, vProtocolAtts))
|
||||
{
|
||||
paramValue = processParamProtocol(paramValue);
|
||||
}
|
||||
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (inArray(name, vSelfClosingTags))
|
||||
{
|
||||
ending = " /";
|
||||
}
|
||||
|
||||
if (inArray(name, vNeedClosingTags))
|
||||
{
|
||||
ending = "";
|
||||
}
|
||||
|
||||
if (ending == null || ending.length() < 1)
|
||||
{
|
||||
if (vTagCounts.containsKey(name))
|
||||
{
|
||||
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
vTagCounts.put(name, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ending = " /";
|
||||
}
|
||||
return "<" + name + params + ending + ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// comments
|
||||
m = P_COMMENT.matcher(s);
|
||||
if (!stripComment && m.find())
|
||||
{
|
||||
return "<" + m.group() + ">";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private String processParamProtocol(String s)
|
||||
{
|
||||
s = decodeEntities(s);
|
||||
final Matcher m = P_PROTOCOL.matcher(s);
|
||||
if (m.find())
|
||||
{
|
||||
final String protocol = m.group(1);
|
||||
if (!inArray(protocol, vAllowedProtocols))
|
||||
{
|
||||
// bad protocol, turn into local anchor link instead
|
||||
s = "#" + s.substring(protocol.length() + 1);
|
||||
if (s.startsWith("#//"))
|
||||
{
|
||||
s = "#" + s.substring(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String decodeEntities(String s)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
Matcher m = P_ENTITY.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.decode(match).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENTITY_UNICODE.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENCODE.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
s = validateEntities(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private String validateEntities(final String s)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
// validate entities throughout the string
|
||||
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
final String one = m.group(1); // ([^&;]*)
|
||||
final String two = m.group(2); // (?=(;|&|$))
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return encodeQuotes(buf.toString());
|
||||
}
|
||||
|
||||
private String encodeQuotes(final String s)
|
||||
{
|
||||
if (encodeQuotes)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
Matcher m = P_VALID_QUOTES.matcher(s);
|
||||
while (m.find())
|
||||
{
|
||||
final String one = m.group(1); // (>|^)
|
||||
final String two = m.group(2); // ([^<]+?)
|
||||
final String three = m.group(3); // (<|$)
|
||||
// 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
return buf.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private String checkEntity(final String preamble, final String term)
|
||||
{
|
||||
|
||||
return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
|
||||
}
|
||||
|
||||
private boolean isValidEntity(final String entity)
|
||||
{
|
||||
return inArray(entity, vAllowedEntities);
|
||||
}
|
||||
|
||||
private static boolean inArray(final String s, final String[] array)
|
||||
{
|
||||
for (String item : array)
|
||||
{
|
||||
if (item != null && item.equals(s))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean allowed(final String name)
|
||||
{
|
||||
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
|
||||
}
|
||||
|
||||
private boolean allowedAttribute(final String name, final String paramName)
|
||||
{
|
||||
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.fastbee.common.utils.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.servlet.ServletRequest;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 通用http工具封装
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class HttpHelper
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
|
||||
|
||||
public static String getBodyString(ServletRequest request)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try (InputStream inputStream = request.getInputStream())
|
||||
{
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
String line = "";
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
sb.append(line);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("getBodyString出现问题!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error(ExceptionUtils.getMessage(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package com.fastbee.common.utils.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 通用http发送方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class HttpUtils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url)
|
||||
{
|
||||
return sendGet(url, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url, String param)
|
||||
{
|
||||
return sendGet(url, param, Constants.UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送GET方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||
* @param contentType 编码类型
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendGet(String url, String param, String contentType)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
BufferedReader in = null;
|
||||
try
|
||||
{
|
||||
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
|
||||
log.info("sendGet - {}", urlNameString);
|
||||
URL realUrl = new URL(urlNameString);
|
||||
URLConnection connection = realUrl.openConnection();
|
||||
connection.setRequestProperty("accept", "*/*");
|
||||
connection.setRequestProperty("connection", "Keep-Alive");
|
||||
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
connection.connect();
|
||||
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null)
|
||||
{
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
}
|
||||
catch (ConnectException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (SocketTimeoutException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (in != null)
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送POST方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @param param 请求参数,请求参数应该是 JSON String格式 的形式。
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendPost(String url, String param)
|
||||
{
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
try
|
||||
{
|
||||
log.info("sendPost - {}", url);
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("contentType", "utf-8");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(param);
|
||||
out.flush();
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null)
|
||||
{
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
}
|
||||
catch (ConnectException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (SocketTimeoutException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (out != null)
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
if (in != null)
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String sendSSLPost(String url, String param)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
String urlNameString = url + "?" + param;
|
||||
try
|
||||
{
|
||||
log.info("sendSSLPost - {}", urlNameString);
|
||||
SSLContext sc = SSLContext.getInstance("SSL");
|
||||
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
|
||||
URL console = new URL(urlNameString);
|
||||
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("contentType", "utf-8");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
|
||||
conn.setSSLSocketFactory(sc.getSocketFactory());
|
||||
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
|
||||
conn.connect();
|
||||
InputStream is = conn.getInputStream();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
String ret = "";
|
||||
while ((ret = br.readLine()) != null)
|
||||
{
|
||||
if (ret != null && !"".equals(ret.trim()))
|
||||
{
|
||||
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
conn.disconnect();
|
||||
br.close();
|
||||
}
|
||||
catch (ConnectException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (SocketTimeoutException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String sendJsonPost(String url, String json) throws IOException {
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
StringBuilder result = new StringBuilder();
|
||||
try
|
||||
{
|
||||
log.info("sendPost - {}", url);
|
||||
URL realUrl = new URL(url);
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
out.print(json);
|
||||
out.flush();
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null)
|
||||
{
|
||||
result.append(line);
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
}
|
||||
catch (ConnectException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + json, e);
|
||||
}
|
||||
catch (SocketTimeoutException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + json, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + json, e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + json, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (out != null)
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
if (in != null)
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + json, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static class TrustAnyTrustManager implements X509TrustManager
|
||||
{
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers()
|
||||
{
|
||||
return new X509Certificate[] {};
|
||||
}
|
||||
}
|
||||
|
||||
private static class TrustAnyHostnameVerifier implements HostnameVerifier
|
||||
{
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.fastbee.common.utils.ip;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.config.RuoYiConfig;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.http.HttpUtils;
|
||||
|
||||
/**
|
||||
* 获取地址类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class AddressUtils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
|
||||
|
||||
// IP地址查询
|
||||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||
|
||||
// 未知地址
|
||||
public static final String UNKNOWN = "XX XX";
|
||||
|
||||
public static String getRealAddressByIP(String ip)
|
||||
{
|
||||
// 内网不查询
|
||||
if (IpUtils.internalIp(ip))
|
||||
{
|
||||
return "内网IP";
|
||||
}
|
||||
if (RuoYiConfig.isAddressEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
|
||||
if (StringUtils.isEmpty(rspStr))
|
||||
{
|
||||
log.error("获取地理位置异常 {}", ip);
|
||||
return UNKNOWN;
|
||||
}
|
||||
JSONObject obj = JSON.parseObject(rspStr);
|
||||
String region = obj.getString("pro");
|
||||
String city = obj.getString("city");
|
||||
return String.format("%s %s", region, city);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取地理位置异常 {}", ip);
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package com.fastbee.common.utils.ip;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 获取IP方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class IpUtils
|
||||
{
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getIpAddr(HttpServletRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
|
||||
{
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
|
||||
{
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
|
||||
{
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
|
||||
{
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
|
||||
{
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param ip IP地址
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean internalIp(String ip)
|
||||
{
|
||||
byte[] addr = textToNumericFormatV4(ip);
|
||||
return internalIp(addr) || "127.0.0.1".equals(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param addr byte地址
|
||||
* @return 结果
|
||||
*/
|
||||
private static boolean internalIp(byte[] addr)
|
||||
{
|
||||
if (StringUtils.isNull(addr) || addr.length < 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
final byte b0 = addr[0];
|
||||
final byte b1 = addr[1];
|
||||
// 10.x.x.x/8
|
||||
final byte SECTION_1 = 0x0A;
|
||||
// 172.16.x.x/12
|
||||
final byte SECTION_2 = (byte) 0xAC;
|
||||
final byte SECTION_3 = (byte) 0x10;
|
||||
final byte SECTION_4 = (byte) 0x1F;
|
||||
// 192.168.x.x/16
|
||||
final byte SECTION_5 = (byte) 0xC0;
|
||||
final byte SECTION_6 = (byte) 0xA8;
|
||||
switch (b0)
|
||||
{
|
||||
case SECTION_1:
|
||||
return true;
|
||||
case SECTION_2:
|
||||
if (b1 >= SECTION_3 && b1 <= SECTION_4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case SECTION_5:
|
||||
switch (b1)
|
||||
{
|
||||
case SECTION_6:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IPv4地址转换成字节
|
||||
*
|
||||
* @param text IPv4地址
|
||||
* @return byte 字节
|
||||
*/
|
||||
public static byte[] textToNumericFormatV4(String text)
|
||||
{
|
||||
if (text.length() == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
String[] elements = text.split("\\.", -1);
|
||||
try
|
||||
{
|
||||
long l;
|
||||
int i;
|
||||
switch (elements.length)
|
||||
{
|
||||
case 1:
|
||||
l = Long.parseLong(elements[0]);
|
||||
if ((l < 0L) || (l > 4294967295L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
|
||||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
l = Integer.parseInt(elements[0]);
|
||||
if ((l < 0L) || (l > 255L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l & 0xFF);
|
||||
l = Integer.parseInt(elements[1]);
|
||||
if ((l < 0L) || (l > 16777215L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
l = Integer.parseInt(elements[2]);
|
||||
if ((l < 0L) || (l > 65535L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址
|
||||
*
|
||||
* @return 本地IP地址
|
||||
*/
|
||||
public static String getHostIp()
|
||||
{
|
||||
try
|
||||
{
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
}
|
||||
catch (UnknownHostException e)
|
||||
{
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机名
|
||||
*
|
||||
* @return 本地主机名
|
||||
*/
|
||||
public static String getHostName()
|
||||
{
|
||||
try
|
||||
{
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
}
|
||||
catch (UnknownHostException e)
|
||||
{
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从多级反向代理中获得第一个非unknown IP地址
|
||||
*
|
||||
* @param ip 获得的IP地址
|
||||
* @return 第一个非unknown IP地址
|
||||
*/
|
||||
public static String getMultistageReverseProxyIp(String ip)
|
||||
{
|
||||
// 多级反向代理检测
|
||||
if (ip != null && ip.indexOf(",") > 0)
|
||||
{
|
||||
final String[] ips = ip.trim().split(",");
|
||||
for (String subIp : ips)
|
||||
{
|
||||
if (false == isUnknown(subIp))
|
||||
{
|
||||
ip = subIp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
|
||||
*
|
||||
* @param checkString 被检测的字符串
|
||||
* @return 是否未知
|
||||
*/
|
||||
public static boolean isUnknown(String checkString)
|
||||
{
|
||||
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.fastbee.common.utils.json;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
*
|
||||
* @author fastbee
|
||||
*/
|
||||
@UtilityClass
|
||||
@Slf4j
|
||||
public class JsonUtils {
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 objectMapper 属性
|
||||
* <p>
|
||||
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
|
||||
*
|
||||
* @param objectMapper ObjectMapper 对象
|
||||
*/
|
||||
public static void init(ObjectMapper objectMapper) {
|
||||
JsonUtils.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonString(Object object) {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] toJsonByte(Object object) {
|
||||
return objectMapper.writeValueAsBytes(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonPrettyString(Object object) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Type type) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串解析成指定类型的对象
|
||||
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
|
||||
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param clazz 类型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T parseObject2(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
return JSONUtil.toBean(text, clazz);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||
if (ArrayUtil.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(bytes, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", bytes, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(String text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(byte[] text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJson(String text) {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.fastbee.common.utils.modbus;
|
||||
|
||||
/**
|
||||
*
|
||||
* @description 位运算工具
|
||||
* 用途:将二进制数中的每位数字1或0代表着某种开关标记,1为是,0为否,则一个数字可以代表N位的开关标记值,可有效减少过多的变量定义 或 过多的表字段
|
||||
*/
|
||||
public class BitUtils {
|
||||
|
||||
/**
|
||||
* 获取二进制数字中指定位数的结果,如:1011,指定第2位,则结果是0,第3位,则结果是1
|
||||
*
|
||||
* @param num 二进制数(可以十进制数传入,也可使用0b开头的二进制数表示形式)
|
||||
* @param bit 位数(第几位,从右往左,从0开始)
|
||||
* @return
|
||||
*/
|
||||
public static int getBitFlag(long num, int bit) {
|
||||
return (int) num >> bit & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新二进制数字中指定位的值
|
||||
*
|
||||
* @param num 二进制数(可以十进制数传入,也可使用0b开头的二进制数表示形式)
|
||||
* @param bit 位数(第几位,从右往左,从0开始)
|
||||
* @param flagValue 位标记值(true=1,false=0)
|
||||
* @return
|
||||
*/
|
||||
public static long updateBitValue(long num, int bit, boolean flagValue) {
|
||||
if (flagValue) {
|
||||
//将某位由0改为1
|
||||
return num | (1 << bit);
|
||||
} else {
|
||||
//将某位由1改为0
|
||||
return num ^ (getBitFlag(num, bit) << bit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字转换为二制值形式字符串
|
||||
*
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static String toBinaryString(long num) {
|
||||
return Long.toBinaryString(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断10进制数,某位是0还是1
|
||||
* @param num
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static int deter(int num, int i) {
|
||||
// 先将数字右移指定第i位,然后再用&与1运算
|
||||
i += 1;
|
||||
return num >> (i-1) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断hex数据,某位的值
|
||||
* @param hex
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public static int deterHex(String hex,int i){
|
||||
return deter(Integer.parseInt(hex,16),i);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int deter = deter(7, 0);
|
||||
int deterHex = deterHex("10", 4);
|
||||
System.out.println(deter);
|
||||
System.out.println(deterHex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.fastbee.common.utils.modbus;
|
||||
|
||||
import com.fastbee.common.core.protocol.modbus.ModbusCode;
|
||||
|
||||
/**
|
||||
* @author gsb
|
||||
* @date 2024/6/15 11:21
|
||||
*/
|
||||
public class ModbusUtils {
|
||||
|
||||
/**
|
||||
* 获取modbus功能码
|
||||
* isReadOnly: 0-读写 1-只读
|
||||
* type: 1-IO寄存器 2-数据寄存器
|
||||
* IO寄存器读写 05功能码 数据寄存器只读 06功能码
|
||||
* @param type modbus数据类型
|
||||
* @return modbus功能码
|
||||
*/
|
||||
public static ModbusCode getModbusCode(int type){
|
||||
if (type == 1){
|
||||
return ModbusCode.Write05;
|
||||
}else {
|
||||
return ModbusCode.Write06;
|
||||
}
|
||||
}
|
||||
|
||||
public static ModbusCode getReadModbusCode(int type,int isReadOnly){
|
||||
if (type == 1){
|
||||
return isReadOnly == 0 ? ModbusCode.Read01 : ModbusCode.Read02;
|
||||
}else {
|
||||
return isReadOnly == 0 ? ModbusCode.Read03 : ModbusCode.Read04;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取modbus-hex字符串寄存器地址
|
||||
* @param hexString hex字符串
|
||||
* @return 寄存器地址-10进制
|
||||
*/
|
||||
public static int getModbusAddress(String hexString){
|
||||
return Integer.parseInt(hexString.substring(4, 8),16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从机地址
|
||||
* @param hexString
|
||||
* @return
|
||||
*/
|
||||
public static int getModbusSlaveId(String hexString){
|
||||
return Integer.parseInt(hexString.substring(0,2),16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取功能码
|
||||
* @param hexString
|
||||
* @return
|
||||
*/
|
||||
public static int getModbusCode(String hexString){
|
||||
return Integer.parseInt(hexString.substring(2,4),16);
|
||||
}
|
||||
|
||||
public static Mparams getModbusParams(String hexString){
|
||||
Mparams mparams = new Mparams();
|
||||
mparams.setSlaveId(Integer.parseInt(hexString.substring(0,2),16));
|
||||
mparams.setCode(Integer.parseInt(hexString.substring(2,4),16));
|
||||
mparams.setAddress(Integer.parseInt(hexString.substring(4, 8),16));
|
||||
return mparams;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
int modbusAddress = getModbusAddress("0101000A0001FDCA");
|
||||
System.out.println(modbusAddress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.fastbee.common.utils.modbus;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author bill
|
||||
*/
|
||||
@Data
|
||||
public class Mparams {
|
||||
|
||||
private int slaveId;
|
||||
|
||||
private int code;
|
||||
|
||||
private int address;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.fastbee.common.utils.object;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Object 工具类
|
||||
*
|
||||
* @author fastbee
|
||||
*/
|
||||
public class ObjectUtils {
|
||||
|
||||
/**
|
||||
* 复制对象,并忽略 Id 编号
|
||||
*
|
||||
* @param object 被复制对象
|
||||
* @param consumer 消费者,可以二次编辑被复制对象
|
||||
* @return 复制后的对象
|
||||
*/
|
||||
public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {
|
||||
T result = ObjectUtil.clone(object);
|
||||
// 忽略 id 编号
|
||||
Field field = ReflectUtil.getField(object.getClass(), "id");
|
||||
if (field != null) {
|
||||
ReflectUtil.setFieldValue(result, field, null);
|
||||
}
|
||||
// 二次编辑
|
||||
if (result != null) {
|
||||
consumer.accept(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends Comparable<T>> T max(T obj1, T obj2) {
|
||||
if (obj1 == null) {
|
||||
return obj2;
|
||||
}
|
||||
if (obj2 == null) {
|
||||
return obj1;
|
||||
}
|
||||
return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> T defaultIfNull(T... array) {
|
||||
for (T item : array) {
|
||||
if (item != null) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> boolean equalsAny(T obj, T... array) {
|
||||
return Arrays.asList(array).contains(obj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.fastbee.common.utils.poi;
|
||||
|
||||
/**
|
||||
* Excel数据格式处理适配器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface ExcelHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* 格式化
|
||||
*
|
||||
* @param value 单元格数据值
|
||||
* @param args excel注解args参数组
|
||||
*
|
||||
* @return 处理后的值
|
||||
*/
|
||||
Object format(Object value, String[] args);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,410 @@
|
||||
package com.fastbee.common.utils.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.fastbee.common.core.text.Convert;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
|
||||
/**
|
||||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ReflectUtils
|
||||
{
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||
|
||||
/**
|
||||
* 调用Getter方法.
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeGetter(Object obj, String propertyName)
|
||||
{
|
||||
Object object = obj;
|
||||
for (String name : StringUtils.split(propertyName, "."))
|
||||
{
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
|
||||
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
|
||||
}
|
||||
return (E) object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法, 仅匹配方法名。
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
public static <E> void invokeSetter(Object obj, String propertyName, E value)
|
||||
{
|
||||
Object object = obj;
|
||||
String[] names = StringUtils.split(propertyName, ".");
|
||||
for (int i = 0; i < names.length; i++)
|
||||
{
|
||||
if (i < names.length - 1)
|
||||
{
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
|
||||
}
|
||||
else
|
||||
{
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
invokeMethodByName(object, setterMethodName, new Object[] { value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getFieldValue(final Object obj, final String fieldName)
|
||||
{
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null)
|
||||
{
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return null;
|
||||
}
|
||||
E result = null;
|
||||
try
|
||||
{
|
||||
result = (E) field.get(obj);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||
*/
|
||||
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
|
||||
{
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null)
|
||||
{
|
||||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
field.set(obj, value);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符.
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||
* 同时匹配方法名+参数类型,
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||
final Object[] args)
|
||||
{
|
||||
if (obj == null || methodName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||
if (method == null)
|
||||
{
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return (E) method.invoke(obj, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符,
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
|
||||
{
|
||||
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||
if (method == null)
|
||||
{
|
||||
// 如果为空不报错,直接返回空。
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||
Class<?>[] cs = method.getParameterTypes();
|
||||
for (int i = 0; i < cs.length; i++)
|
||||
{
|
||||
if (args[i] != null && !args[i].getClass().equals(cs[i]))
|
||||
{
|
||||
if (cs[i] == String.class)
|
||||
{
|
||||
args[i] = Convert.toStr(args[i]);
|
||||
if (StringUtils.endsWith((String) args[i], ".0"))
|
||||
{
|
||||
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||
}
|
||||
}
|
||||
else if (cs[i] == Integer.class)
|
||||
{
|
||||
args[i] = Convert.toInt(args[i]);
|
||||
}
|
||||
else if (cs[i] == Long.class)
|
||||
{
|
||||
args[i] = Convert.toLong(args[i]);
|
||||
}
|
||||
else if (cs[i] == Double.class)
|
||||
{
|
||||
args[i] = Convert.toDouble(args[i]);
|
||||
}
|
||||
else if (cs[i] == Float.class)
|
||||
{
|
||||
args[i] = Convert.toFloat(args[i]);
|
||||
}
|
||||
else if (cs[i] == Date.class)
|
||||
{
|
||||
if (args[i] instanceof String)
|
||||
{
|
||||
args[i] = DateUtils.parseDate(args[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||
}
|
||||
}
|
||||
else if (cs[i] == boolean.class || cs[i] == Boolean.class)
|
||||
{
|
||||
args[i] = Convert.toBool(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (E) method.invoke(obj, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
*/
|
||||
public static Field getAccessibleField(final Object obj, final String fieldName)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
makeAccessible(field);
|
||||
return field;
|
||||
}
|
||||
catch (NoSuchFieldException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 匹配函数名+参数类型。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethod(final Object obj, final String methodName,
|
||||
final Class<?>... parameterTypes)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
|
||||
{
|
||||
try
|
||||
{
|
||||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 只匹配函数名。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
|
||||
{
|
||||
Method[] methods = searchType.getDeclaredMethods();
|
||||
for (Method method : methods)
|
||||
{
|
||||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
|
||||
{
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Method method)
|
||||
{
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible())
|
||||
{
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Field field)
|
||||
{
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
|
||||
{
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getClassGenricType(final Class clazz)
|
||||
{
|
||||
return getClassGenricType(clazz, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
public static Class getClassGenricType(final Class clazz, final int index)
|
||||
{
|
||||
Type genType = clazz.getGenericSuperclass();
|
||||
|
||||
if (!(genType instanceof ParameterizedType))
|
||||
{
|
||||
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||
|
||||
if (index >= params.length || index < 0)
|
||||
{
|
||||
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
|
||||
+ params.length);
|
||||
return Object.class;
|
||||
}
|
||||
if (!(params[index] instanceof Class))
|
||||
{
|
||||
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
return (Class) params[index];
|
||||
}
|
||||
|
||||
public static Class<?> getUserClass(Object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw new RuntimeException("Instance must not be null");
|
||||
}
|
||||
Class clazz = instance.getClass();
|
||||
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
|
||||
{
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null && !Object.class.equals(superClass))
|
||||
{
|
||||
return superClass;
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反射时的checked exception转换为unchecked exception.
|
||||
*/
|
||||
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
|
||||
{
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException)
|
||||
{
|
||||
return new IllegalArgumentException(msg, e);
|
||||
}
|
||||
else if (e instanceof InvocationTargetException)
|
||||
{
|
||||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||
}
|
||||
return new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package com.fastbee.common.utils.sign;
|
||||
|
||||
/**
|
||||
* Base64工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public final class Base64
|
||||
{
|
||||
static private final int BASELENGTH = 128;
|
||||
static private final int LOOKUPLENGTH = 64;
|
||||
static private final int TWENTYFOURBITGROUP = 24;
|
||||
static private final int EIGHTBIT = 8;
|
||||
static private final int SIXTEENBIT = 16;
|
||||
static private final int FOURBYTE = 4;
|
||||
static private final int SIGN = -128;
|
||||
static private final char PAD = '=';
|
||||
static final private byte[] base64Alphabet = new byte[BASELENGTH];
|
||||
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
|
||||
|
||||
static
|
||||
{
|
||||
for (int i = 0; i < BASELENGTH; ++i)
|
||||
{
|
||||
base64Alphabet[i] = -1;
|
||||
}
|
||||
for (int i = 'Z'; i >= 'A'; i--)
|
||||
{
|
||||
base64Alphabet[i] = (byte) (i - 'A');
|
||||
}
|
||||
for (int i = 'z'; i >= 'a'; i--)
|
||||
{
|
||||
base64Alphabet[i] = (byte) (i - 'a' + 26);
|
||||
}
|
||||
|
||||
for (int i = '9'; i >= '0'; i--)
|
||||
{
|
||||
base64Alphabet[i] = (byte) (i - '0' + 52);
|
||||
}
|
||||
|
||||
base64Alphabet['+'] = 62;
|
||||
base64Alphabet['/'] = 63;
|
||||
|
||||
for (int i = 0; i <= 25; i++)
|
||||
{
|
||||
lookUpBase64Alphabet[i] = (char) ('A' + i);
|
||||
}
|
||||
|
||||
for (int i = 26, j = 0; i <= 51; i++, j++)
|
||||
{
|
||||
lookUpBase64Alphabet[i] = (char) ('a' + j);
|
||||
}
|
||||
|
||||
for (int i = 52, j = 0; i <= 61; i++, j++)
|
||||
{
|
||||
lookUpBase64Alphabet[i] = (char) ('0' + j);
|
||||
}
|
||||
lookUpBase64Alphabet[62] = (char) '+';
|
||||
lookUpBase64Alphabet[63] = (char) '/';
|
||||
}
|
||||
|
||||
private static boolean isWhiteSpace(char octect)
|
||||
{
|
||||
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
|
||||
}
|
||||
|
||||
private static boolean isPad(char octect)
|
||||
{
|
||||
return (octect == PAD);
|
||||
}
|
||||
|
||||
private static boolean isData(char octect)
|
||||
{
|
||||
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes hex octects into Base64
|
||||
*
|
||||
* @param binaryData Array containing binaryData
|
||||
* @return Encoded Base64 array
|
||||
*/
|
||||
public static String encode(byte[] binaryData)
|
||||
{
|
||||
if (binaryData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int lengthDataBits = binaryData.length * EIGHTBIT;
|
||||
if (lengthDataBits == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
|
||||
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
|
||||
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
|
||||
char encodedData[] = null;
|
||||
|
||||
encodedData = new char[numberQuartet * 4];
|
||||
|
||||
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
|
||||
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
|
||||
for (int i = 0; i < numberTriplets; i++)
|
||||
{
|
||||
b1 = binaryData[dataIndex++];
|
||||
b2 = binaryData[dataIndex++];
|
||||
b3 = binaryData[dataIndex++];
|
||||
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||
}
|
||||
|
||||
// form integral number of 6-bit groups
|
||||
if (fewerThan24bits == EIGHTBIT)
|
||||
{
|
||||
b1 = binaryData[dataIndex];
|
||||
k = (byte) (b1 & 0x03);
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
}
|
||||
else if (fewerThan24bits == SIXTEENBIT)
|
||||
{
|
||||
b1 = binaryData[dataIndex];
|
||||
b2 = binaryData[dataIndex + 1];
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
}
|
||||
return new String(encodedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octects
|
||||
*
|
||||
* @param encoded string containing Base64 data
|
||||
* @return Array containind decoded data.
|
||||
*/
|
||||
public static byte[] decode(String encoded)
|
||||
{
|
||||
if (encoded == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
char[] base64Data = encoded.toCharArray();
|
||||
// remove white spaces
|
||||
int len = removeWhiteSpace(base64Data);
|
||||
|
||||
if (len % FOURBYTE != 0)
|
||||
{
|
||||
return null;// should be divisible by four
|
||||
}
|
||||
|
||||
int numberQuadruple = (len / FOURBYTE);
|
||||
|
||||
if (numberQuadruple == 0)
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
byte decodedData[] = null;
|
||||
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
||||
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
|
||||
|
||||
int i = 0;
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
decodedData = new byte[(numberQuadruple) * 3];
|
||||
|
||||
for (; i < numberQuadruple - 1; i++)
|
||||
{
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|
||||
|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++])))
|
||||
{
|
||||
return null;
|
||||
} // if found "no data" just return null
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
}
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])))
|
||||
{
|
||||
return null;// if found "no data" just return null
|
||||
}
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
|
||||
d3 = base64Data[dataIndex++];
|
||||
d4 = base64Data[dataIndex++];
|
||||
if (!isData((d3)) || !isData((d4)))
|
||||
{// Check if they are PAD characters
|
||||
if (isPad(d3) && isPad(d4))
|
||||
{
|
||||
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 1];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
|
||||
return tmp;
|
||||
}
|
||||
else if (!isPad(d3) && isPad(d4))
|
||||
{
|
||||
b3 = base64Alphabet[d3];
|
||||
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 2];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
return tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // No PAD e.g 3cQl
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
|
||||
}
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||
*
|
||||
* @param data the byte array of base64 data (with WS)
|
||||
* @return the new length
|
||||
*/
|
||||
private static int removeWhiteSpace(char[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// count characters that's not whitespace
|
||||
int newSize = 0;
|
||||
int len = data.length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (!isWhiteSpace(data[i]))
|
||||
{
|
||||
data[newSize++] = data[i];
|
||||
}
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.fastbee.common.utils.sign;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Md5加密方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Md5Utils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
|
||||
|
||||
private static byte[] md5(String s)
|
||||
{
|
||||
MessageDigest algorithm;
|
||||
try
|
||||
{
|
||||
algorithm = MessageDigest.getInstance("MD5");
|
||||
algorithm.reset();
|
||||
algorithm.update(s.getBytes("UTF-8"));
|
||||
byte[] messageDigest = algorithm.digest();
|
||||
return messageDigest;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("MD5 Error...", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final String toHex(byte hash[])
|
||||
{
|
||||
if (hash == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(hash.length * 2);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hash.length; i++)
|
||||
{
|
||||
if ((hash[i] & 0xff) < 0x10)
|
||||
{
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Long.toString(hash[i] & 0xff, 16));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String hash(String s)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("not supported charset...{}", e);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.fastbee.common.utils.sign;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author fastb
|
||||
* @version 1.0
|
||||
* @description: 签名验证
|
||||
* @date 2024-03-12 15:54
|
||||
*/
|
||||
public class SignUtils {
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*/
|
||||
public static boolean checkSignature(String token, String signature, String timestamp,String nonce) {
|
||||
// 1.将token、timestamp、nonce三个参数进行字典序排序
|
||||
String[] arr = new String[] { token, timestamp, nonce };
|
||||
Arrays.sort(arr);
|
||||
|
||||
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
|
||||
StringBuilder content = new StringBuilder();
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
content.append(arr[i]);
|
||||
}
|
||||
MessageDigest md = null;
|
||||
String tmpStr = null;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-1");
|
||||
// 将三个参数字符串拼接成一个字符串进行sha1加密
|
||||
byte[] digest = md.digest(content.toString().getBytes());
|
||||
tmpStr = byteToStr(digest);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
content = null;
|
||||
// 3.将sha1加密后的字符串可与signature对比,标识该请求来源于微信
|
||||
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节数组转换为十六进制字符串
|
||||
*/
|
||||
private static String byteToStr(byte[] byteArray) {
|
||||
String strDigest = "";
|
||||
for (int i = 0; i < byteArray.length; i++) {
|
||||
strDigest += byteToHexStr(byteArray[i]);
|
||||
}
|
||||
return strDigest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字节转换为十六进制字符串
|
||||
*/
|
||||
private static String byteToHexStr(byte mByte) {
|
||||
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };
|
||||
char[] tempArr = new char[2];
|
||||
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
|
||||
tempArr[1] = Digit[mByte & 0X0F];
|
||||
String s = new String(tempArr);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.fastbee.common.utils.spring;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
|
||||
{
|
||||
/** Spring应用上下文环境 */
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
SpringUtils.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||
{
|
||||
SpringUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException
|
||||
{
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
* @return
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException
|
||||
{
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name)
|
||||
{
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return Class 注册对象的类型
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker)
|
||||
{
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,无配置返回null
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String[] getActiveProfiles()
|
||||
{
|
||||
return applicationContext.getEnvironment().getActiveProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String getActiveProfile()
|
||||
{
|
||||
final String[] activeProfiles = getActiveProfiles();
|
||||
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置文件中的值
|
||||
*
|
||||
* @param key 配置文件的key
|
||||
* @return 当前的配置文件的值
|
||||
*
|
||||
*/
|
||||
public static String getRequiredProperty(String key)
|
||||
{
|
||||
return applicationContext.getEnvironment().getRequiredProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取带有annotation注解的所有bean集合
|
||||
* @param annotation 注解
|
||||
* @param <T>
|
||||
* @return 集合
|
||||
*/
|
||||
public static <T> Map<String,T> getBeanWithAnnotation(Class<? extends Annotation> annotation){
|
||||
Map<String ,T> resultMap = new HashMap<>();
|
||||
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(annotation);
|
||||
if (CollectionUtils.isEmpty(beanMap)){
|
||||
return resultMap;
|
||||
}
|
||||
beanMap.forEach((key,value)->{
|
||||
resultMap.put(key,(T) value);
|
||||
});
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.fastbee.common.utils.sql;
|
||||
|
||||
import com.fastbee.common.exception.UtilException;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SqlUtil
|
||||
{
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql(String value)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
|
||||
{
|
||||
throw new UtilException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 order by 语法是否符合规范
|
||||
*/
|
||||
public static boolean isValidOrderBySql(String value)
|
||||
{
|
||||
return value.matches(SQL_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL关键字检查
|
||||
*/
|
||||
public static void filterKeyword(String value)
|
||||
{
|
||||
if (StringUtils.isEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords)
|
||||
{
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
|
||||
{
|
||||
throw new UtilException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.fastbee.common.utils.uuid;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.fastbee.common.utils.Md5Utils;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* ID生成器工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
public class IdUtils
|
||||
{
|
||||
private static long lastTimestamp = -1L;
|
||||
private long sequence = 0L;
|
||||
private final long workerId;
|
||||
private final long datacenterId;
|
||||
private static Integer startIndex=0;
|
||||
private static Integer endIndex=6;
|
||||
|
||||
public IdUtils(long workerId, long datacenterId) {
|
||||
if(workerId <= 31L && workerId >= 0L) {
|
||||
this.workerId = workerId;
|
||||
} else {
|
||||
if(workerId != -1L) {
|
||||
throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
|
||||
}
|
||||
|
||||
this.workerId = (long)(new Random()).nextInt(31);
|
||||
}
|
||||
|
||||
if(datacenterId <= 31L && datacenterId >= 0L) {
|
||||
this.datacenterId = datacenterId;
|
||||
} else {
|
||||
if(datacenterId != -1L) {
|
||||
throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
|
||||
}
|
||||
|
||||
this.datacenterId = (long)(new Random()).nextInt(31);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public synchronized long nextId() {
|
||||
long timestamp = this.timeGen();
|
||||
if(timestamp < lastTimestamp) {
|
||||
try {
|
||||
throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
|
||||
} catch (Exception e) {
|
||||
log.warn("生成ID异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
if(lastTimestamp == timestamp) {
|
||||
this.sequence = this.sequence + 1L & 4095L;
|
||||
if(this.sequence == 0L) {
|
||||
timestamp = this.tilNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
this.sequence = 0L;
|
||||
}
|
||||
|
||||
lastTimestamp = timestamp;
|
||||
return timestamp - 1288834974657L << 22 | this.datacenterId << 17 | this.workerId << 12 | this.sequence;
|
||||
}
|
||||
|
||||
private long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp;
|
||||
for(timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {
|
||||
;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static String uuid() {
|
||||
return java.util.UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
public static String getNextCode() {
|
||||
return Md5Utils.md5(IdUtils.uuid() + System.currentTimeMillis()).substring(startIndex,endIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取随机UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String randomUUID()
|
||||
{
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String simpleUUID()
|
||||
{
|
||||
return UUID.randomUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String fastUUID()
|
||||
{
|
||||
return UUID.fastUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String fastSimpleUUID()
|
||||
{
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.fastbee.common.utils.uuid;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ruoyi 序列生成类
|
||||
*/
|
||||
public class Seq
|
||||
{
|
||||
// 通用序列类型
|
||||
public static final String commSeqType = "COMMON";
|
||||
|
||||
// 上传序列类型
|
||||
public static final String uploadSeqType = "UPLOAD";
|
||||
|
||||
// 通用接口序列数
|
||||
private static AtomicInteger commSeq = new AtomicInteger(1);
|
||||
|
||||
// 上传接口序列数
|
||||
private static AtomicInteger uploadSeq = new AtomicInteger(1);
|
||||
|
||||
// 机器标识
|
||||
private static String machineCode = "A";
|
||||
|
||||
/**
|
||||
* 获取通用序列号
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId()
|
||||
{
|
||||
return getId(commSeqType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId(String type)
|
||||
{
|
||||
AtomicInteger atomicInt = commSeq;
|
||||
if (uploadSeqType.equals(type))
|
||||
{
|
||||
atomicInt = uploadSeq;
|
||||
}
|
||||
return getId(atomicInt, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
|
||||
*
|
||||
* @param atomicInt 序列数
|
||||
* @param length 数值长度
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId(AtomicInteger atomicInt, int length)
|
||||
{
|
||||
String result = DateUtils.dateTimeNow();
|
||||
result += machineCode;
|
||||
result += getSeq(atomicInt, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
private synchronized static String getSeq(AtomicInteger atomicInt, int length)
|
||||
{
|
||||
// 先取值再+1
|
||||
int value = atomicInt.getAndIncrement();
|
||||
|
||||
// 如果更新后值>=10 的 (length)幂次方则重置为1
|
||||
int maxSeq = (int) Math.pow(10, length);
|
||||
if (atomicInt.get() >= maxSeq)
|
||||
{
|
||||
atomicInt.set(1);
|
||||
}
|
||||
// 转字符串,用0左补齐
|
||||
return StringUtils.padl(value, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
package com.fastbee.common.utils.uuid;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import com.fastbee.common.exception.UtilException;
|
||||
|
||||
/**
|
||||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
{
|
||||
private static final long serialVersionUID = -1185015143654744140L;
|
||||
|
||||
/**
|
||||
* SecureRandom 的单例
|
||||
*
|
||||
*/
|
||||
private static class Holder
|
||||
{
|
||||
static final SecureRandom numberGenerator = getSecureRandom();
|
||||
}
|
||||
|
||||
/** 此UUID的最高64有效位 */
|
||||
private final long mostSigBits;
|
||||
|
||||
/** 此UUID的最低64有效位 */
|
||||
private final long leastSigBits;
|
||||
|
||||
/**
|
||||
* 私有构造
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
private UUID(byte[] data)
|
||||
{
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
for (int i = 8; i < 16; i++)
|
||||
{
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的数据构造新的 UUID。
|
||||
*
|
||||
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
|
||||
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
|
||||
*/
|
||||
public UUID(long mostSigBits, long leastSigBits)
|
||||
{
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID fastUUID()
|
||||
{
|
||||
return randomUUID(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID()
|
||||
{
|
||||
return randomUUID(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID(boolean isSecure)
|
||||
{
|
||||
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
ng.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(randomBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
|
||||
*
|
||||
* @param name 用于构造 UUID 的字节数组。
|
||||
*
|
||||
* @return 根据指定数组生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID nameUUIDFromBytes(byte[] name)
|
||||
{
|
||||
MessageDigest md;
|
||||
try
|
||||
{
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
}
|
||||
catch (NoSuchAlgorithmException nsae)
|
||||
{
|
||||
throw new InternalError("MD5 not supported");
|
||||
}
|
||||
byte[] md5Bytes = md.digest(name);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||
*
|
||||
* @param name 指定 {@code UUID} 字符串
|
||||
* @return 具有指定值的 {@code UUID}
|
||||
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
|
||||
*
|
||||
*/
|
||||
public static UUID fromString(String name)
|
||||
{
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||
}
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
components[i] = "0x" + components[i];
|
||||
}
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new UUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*/
|
||||
public long getLeastSignificantBits()
|
||||
{
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最高有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中最高有效 64 位。
|
||||
*/
|
||||
public long getMostSignificantBits()
|
||||
{
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
|
||||
* <p>
|
||||
* 版本号具有以下含意:
|
||||
* <ul>
|
||||
* <li>1 基于时间的 UUID
|
||||
* <li>2 DCE 安全 UUID
|
||||
* <li>3 基于名称的 UUID
|
||||
* <li>4 随机生成的 UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 的版本号
|
||||
*/
|
||||
public int version()
|
||||
{
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
|
||||
* <p>
|
||||
* 变体号具有以下含意:
|
||||
* <ul>
|
||||
* <li>0 为 NCS 向后兼容保留
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
|
||||
* <li>6 保留,微软向后兼容
|
||||
* <li>7 保留供以后定义使用
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 相关联的变体号
|
||||
*/
|
||||
public int variant()
|
||||
{
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时间戳值。
|
||||
*
|
||||
* <p>
|
||||
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
|
||||
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
|
||||
*
|
||||
* <p>
|
||||
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
|
||||
*/
|
||||
public long timestamp() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return (mostSigBits & 0x0FFFL) << 48//
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时钟序列值。
|
||||
*
|
||||
* <p>
|
||||
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
|
||||
* <p>
|
||||
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
|
||||
* UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的时钟序列
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public int clockSequence() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关的节点值。
|
||||
*
|
||||
* <p>
|
||||
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
|
||||
* <p>
|
||||
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的节点值
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public long node() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return leastSigBits & 0x0000FFFFFFFFFFFFL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
* @see #toString(boolean)
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
*/
|
||||
public String toString(boolean isSimple)
|
||||
{
|
||||
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
|
||||
// time_low
|
||||
builder.append(digits(mostSigBits >> 32, 8));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// time_mid
|
||||
builder.append(digits(mostSigBits >> 16, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// time_high_and_version
|
||||
builder.append(digits(mostSigBits, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// variant_and_sequence
|
||||
builder.append(digits(leastSigBits >> 48, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// node
|
||||
builder.append(digits(leastSigBits, 12));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的哈希码。
|
||||
*
|
||||
* @return UUID 的哈希码值。
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int) (hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将此对象与指定对象比较。
|
||||
* <p>
|
||||
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
|
||||
*
|
||||
* @param obj 要与之比较的对象
|
||||
*
|
||||
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if ((null == obj) || (obj.getClass() != UUID.class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UUID id = (UUID) obj;
|
||||
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
|
||||
/**
|
||||
* 将此 UUID 与指定的 UUID 比较。
|
||||
*
|
||||
* <p>
|
||||
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
|
||||
*
|
||||
* @param val 与此 UUID 比较的 UUID
|
||||
*
|
||||
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(UUID val)
|
||||
{
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||
(this.leastSigBits > val.leastSigBits ? 1 : //
|
||||
0))));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Private method start
|
||||
/**
|
||||
* 返回指定数字对应的hex值
|
||||
*
|
||||
* @param val 值
|
||||
* @param digits 位
|
||||
* @return 值
|
||||
*/
|
||||
private static String digits(long val, int digits)
|
||||
{
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为time-based版本UUID
|
||||
*/
|
||||
private void checkTimeBase()
|
||||
{
|
||||
if (version() != 1)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||
*
|
||||
* @return {@link SecureRandom}
|
||||
*/
|
||||
public static SecureRandom getSecureRandom()
|
||||
{
|
||||
try
|
||||
{
|
||||
return SecureRandom.getInstance("SHA1PRNG");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new UtilException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数生成器对象<br>
|
||||
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
*
|
||||
* @return {@link ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom getRandom()
|
||||
{
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
/**
|
||||
* 加解密异常类
|
||||
*/
|
||||
public class AesException extends Exception {
|
||||
public final static int OK = 0;
|
||||
public final static int ValidateSignatureError = -40001;
|
||||
public final static int ParseXmlError = -40002;
|
||||
public final static int ComputeSignatureError = -40003;
|
||||
public final static int IllegalAesKey = -40004;
|
||||
public final static int ValidateCorpidError = -40005;
|
||||
public final static int EncryptAESError = -40006;
|
||||
public final static int DecryptAESError = -40007;
|
||||
public final static int IllegalBuffer = -40008;
|
||||
//public final static int EncodeBase64Error = -40009;
|
||||
//public final static int DecodeBase64Error = -40010;
|
||||
//public final static int GenReturnXmlError = -40011;
|
||||
private int code;
|
||||
|
||||
private static String getMessage(int code) {
|
||||
switch (code) {
|
||||
case ValidateSignatureError:
|
||||
return "签名验证错误";
|
||||
case ParseXmlError:
|
||||
return "xml解析失败";
|
||||
case ComputeSignatureError:
|
||||
return "sha加密生成签名失败";
|
||||
case IllegalAesKey:
|
||||
return "SymmetricKey非法";
|
||||
case ValidateCorpidError:
|
||||
return "corpid校验失败";
|
||||
case EncryptAESError:
|
||||
return "aes加密失败";
|
||||
case DecryptAESError:
|
||||
return "aes解密失败";
|
||||
case IllegalBuffer:
|
||||
return "解密后得到的buffer非法";
|
||||
// case EncodeBase64Error:
|
||||
// return "base64加密错误";
|
||||
// case DecodeBase64Error:
|
||||
// return "base64解密错误";
|
||||
// case GenReturnXmlError:
|
||||
// return "xml生成失败";
|
||||
default:
|
||||
return null; // cannot be
|
||||
}
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
AesException(int code) {
|
||||
super(getMessage(code));
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class ByteGroup {
|
||||
ArrayList<Byte> byteContainer = new ArrayList<Byte>();
|
||||
|
||||
public byte[] toBytes() {
|
||||
byte[] bytes = new byte[byteContainer.size()];
|
||||
for (int i = 0; i < byteContainer.size(); i++) {
|
||||
bytes[i] = byteContainer.get(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public ByteGroup addBytes(byte[] bytes) {
|
||||
for (byte b : bytes) {
|
||||
byteContainer.add(b);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return byteContainer.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 提供基于PKCS7算法的加解密接口.
|
||||
*/
|
||||
class PKCS7Encoder {
|
||||
static Charset CHARSET = Charset.forName("utf-8");
|
||||
static int BLOCK_SIZE = 32;
|
||||
|
||||
/**
|
||||
* 获得对明文进行补位填充的字节.
|
||||
*
|
||||
* @param count 需要进行填充补位操作的明文字节个数
|
||||
* @return 补齐用的字节数组
|
||||
*/
|
||||
static byte[] encode(int count) {
|
||||
// 计算需要填充的位数
|
||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
|
||||
if (amountToPad == 0) {
|
||||
amountToPad = BLOCK_SIZE;
|
||||
}
|
||||
// 获得补位所用的字符
|
||||
char padChr = chr(amountToPad);
|
||||
String tmp = new String();
|
||||
for (int index = 0; index < amountToPad; index++) {
|
||||
tmp += padChr;
|
||||
}
|
||||
return tmp.getBytes(CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除解密后明文的补位字符
|
||||
*
|
||||
* @param decrypted 解密后的明文
|
||||
* @return 删除补位字符后的明文
|
||||
*/
|
||||
static byte[] decode(byte[] decrypted) {
|
||||
int pad = (int) decrypted[decrypted.length - 1];
|
||||
if (pad < 1 || pad > 32) {
|
||||
pad = 0;
|
||||
}
|
||||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码
|
||||
*
|
||||
* @param a 需要转化的数字
|
||||
* @return 转化得到的字符
|
||||
*/
|
||||
static char chr(int a) {
|
||||
byte target = (byte) (a & 0xFF);
|
||||
return (char) target;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
/**
|
||||
* 对企业微信发送给企业后台的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
/**
|
||||
* SHA1 class
|
||||
*
|
||||
* 计算消息签名接口.
|
||||
*/
|
||||
public class SHA1 {
|
||||
|
||||
/**
|
||||
* 用SHA1算法生成安全签名
|
||||
* @param token 票据
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机字符串
|
||||
* @param encrypt 密文
|
||||
* @return 安全签名
|
||||
* @throws AesException
|
||||
*/
|
||||
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
|
||||
{
|
||||
try {
|
||||
String[] array = new String[] { token, timestamp, nonce, encrypt };
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// 字符串排序
|
||||
Arrays.sort(array);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sb.append(array[i]);
|
||||
}
|
||||
String str = sb.toString();
|
||||
// SHA1签名生成
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
md.update(str.getBytes());
|
||||
byte[] digest = md.digest();
|
||||
|
||||
StringBuffer hexstr = new StringBuffer();
|
||||
String shaHex = "";
|
||||
for (int i = 0; i < digest.length; i++) {
|
||||
shaHex = Integer.toHexString(digest[i] & 0xFF);
|
||||
if (shaHex.length() < 2) {
|
||||
hexstr.append(0);
|
||||
}
|
||||
hexstr.append(shaHex);
|
||||
}
|
||||
return hexstr.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.ComputeSignatureError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
/**
|
||||
* 对企业微信发送给企业后台的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 针对org.apache.commons.codec.binary.Base64,
|
||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
|
||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
|
||||
*/
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串).
|
||||
* <ol>
|
||||
* <li>第三方回复加密消息给企业微信</li>
|
||||
* <li>第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。</li>
|
||||
* </ol>
|
||||
* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
|
||||
* <ol>
|
||||
* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
|
||||
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
|
||||
* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
|
||||
* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>
|
||||
* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class WXBizMsgCrypt {
|
||||
static Charset CHARSET = Charset.forName("utf-8");
|
||||
Base64 base64 = new Base64();
|
||||
byte[] aesKey;
|
||||
String token;
|
||||
String receiveid;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param token 企业微信后台,开发者设置的token
|
||||
* @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey
|
||||
* @param receiveid, 不同场景含义不同,详见文档
|
||||
*
|
||||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
|
||||
*/
|
||||
public WXBizMsgCrypt(String token, String encodingAesKey, String receiveid) throws AesException {
|
||||
if (encodingAesKey.length() != 43) {
|
||||
throw new AesException(AesException.IllegalAesKey);
|
||||
}
|
||||
|
||||
this.token = token;
|
||||
this.receiveid = receiveid;
|
||||
aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
||||
}
|
||||
|
||||
// 生成4个字节的网络字节序
|
||||
byte[] getNetworkBytesOrder(int sourceNumber) {
|
||||
byte[] orderBytes = new byte[4];
|
||||
orderBytes[3] = (byte) (sourceNumber & 0xFF);
|
||||
orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
|
||||
orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
|
||||
orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
|
||||
return orderBytes;
|
||||
}
|
||||
|
||||
// 还原4个字节的网络字节序
|
||||
int recoverNetworkBytesOrder(byte[] orderBytes) {
|
||||
int sourceNumber = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sourceNumber <<= 8;
|
||||
sourceNumber |= orderBytes[i] & 0xff;
|
||||
}
|
||||
return sourceNumber;
|
||||
}
|
||||
|
||||
// 随机生成16位字符串
|
||||
String getRandomStr() {
|
||||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
Random random = new Random();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int number = random.nextInt(base.length());
|
||||
sb.append(base.charAt(number));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对明文进行加密.
|
||||
*
|
||||
* @param text 需要加密的明文
|
||||
* @return 加密后base64编码的字符串
|
||||
* @throws AesException aes加密失败
|
||||
*/
|
||||
String encrypt(String randomStr, String text) throws AesException {
|
||||
ByteGroup byteCollector = new ByteGroup();
|
||||
byte[] randomStrBytes = randomStr.getBytes(CHARSET);
|
||||
byte[] textBytes = text.getBytes(CHARSET);
|
||||
byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
|
||||
byte[] receiveidBytes = receiveid.getBytes(CHARSET);
|
||||
|
||||
// randomStr + networkBytesOrder + text + receiveid
|
||||
byteCollector.addBytes(randomStrBytes);
|
||||
byteCollector.addBytes(networkBytesOrder);
|
||||
byteCollector.addBytes(textBytes);
|
||||
byteCollector.addBytes(receiveidBytes);
|
||||
|
||||
// ... + pad: 使用自定义的填充方式对明文进行补位填充
|
||||
byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
|
||||
byteCollector.addBytes(padBytes);
|
||||
|
||||
// 获得最终的字节流, 未加密
|
||||
byte[] unencrypted = byteCollector.toBytes();
|
||||
|
||||
try {
|
||||
// 设置加密模式为AES的CBC模式
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||
|
||||
// 加密
|
||||
byte[] encrypted = cipher.doFinal(unencrypted);
|
||||
|
||||
// 使用BASE64对加密后的字符串进行编码
|
||||
String base64Encrypted = base64.encodeToString(encrypted);
|
||||
|
||||
return base64Encrypted;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.EncryptAESError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密文进行解密.
|
||||
*
|
||||
* @param text 需要解密的密文
|
||||
* @return 解密得到的明文
|
||||
* @throws AesException aes解密失败
|
||||
*/
|
||||
String decrypt(String text) throws AesException {
|
||||
byte[] original;
|
||||
try {
|
||||
// 设置解密模式为AES的CBC模式
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
|
||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
|
||||
|
||||
// 使用BASE64对密文进行解码
|
||||
byte[] encrypted = Base64.decodeBase64(text);
|
||||
|
||||
// 解密
|
||||
original = cipher.doFinal(encrypted);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.DecryptAESError);
|
||||
}
|
||||
|
||||
String xmlContent, from_receiveid;
|
||||
try {
|
||||
// 去除补位字符
|
||||
byte[] bytes = PKCS7Encoder.decode(original);
|
||||
|
||||
// 分离16位随机字符串,网络字节序和receiveid
|
||||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
|
||||
|
||||
int xmlLength = recoverNetworkBytesOrder(networkOrder);
|
||||
|
||||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
|
||||
from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
|
||||
CHARSET);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.IllegalBuffer);
|
||||
}
|
||||
|
||||
// receiveid不相同的情况
|
||||
if (!from_receiveid.equals(receiveid)) {
|
||||
throw new AesException(AesException.ValidateCorpidError);
|
||||
}
|
||||
return xmlContent;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将企业微信回复用户的消息加密打包.
|
||||
* <ol>
|
||||
* <li>对要发送的消息进行AES-CBC加密</li>
|
||||
* <li>生成安全签名</li>
|
||||
* <li>将消息密文和安全签名打包成xml格式</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param replyMsg 企业微信待回复用户的消息,xml格式的字符串
|
||||
* @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp
|
||||
* @param nonce 随机串,可以自己生成,也可以用URL参数的nonce
|
||||
*
|
||||
* @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串
|
||||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
|
||||
*/
|
||||
public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
|
||||
// 加密
|
||||
String encrypt = encrypt(getRandomStr(), replyMsg);
|
||||
|
||||
// 生成安全签名
|
||||
if (timeStamp == "") {
|
||||
timeStamp = Long.toString(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);
|
||||
|
||||
// System.out.println("发送给平台的签名是: " + signature[1].toString());
|
||||
// 生成发送的xml
|
||||
String result = XMLParse.generate(encrypt, signature, timeStamp, nonce);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验消息的真实性,并且获取解密后的明文.
|
||||
* <ol>
|
||||
* <li>利用收到的密文生成安全签名,进行签名验证</li>
|
||||
* <li>若验证通过,则提取xml中的加密消息</li>
|
||||
* <li>对消息进行解密</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param msgSignature 签名串,对应URL参数的msg_signature
|
||||
* @param timeStamp 时间戳,对应URL参数的timestamp
|
||||
* @param nonce 随机串,对应URL参数的nonce
|
||||
* @param postData 密文,对应POST请求的数据
|
||||
*
|
||||
* @return 解密后的原文
|
||||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
|
||||
*/
|
||||
public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
|
||||
throws AesException {
|
||||
|
||||
// 密钥,公众账号的app secret
|
||||
// 提取密文
|
||||
Object[] encrypt = XMLParse.extract(postData);
|
||||
|
||||
// 验证安全签名
|
||||
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
|
||||
|
||||
// 和URL中的签名比较是否相等
|
||||
// System.out.println("第三方收到URL中的签名:" + msg_sign);
|
||||
// System.out.println("第三方校验签名:" + signature);
|
||||
if (!signature.equals(msgSignature)) {
|
||||
throw new AesException(AesException.ValidateSignatureError);
|
||||
}
|
||||
|
||||
// 解密
|
||||
String result = decrypt(encrypt[1].toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证URL
|
||||
* @param msgSignature 签名串,对应URL参数的msg_signature
|
||||
* @param timeStamp 时间戳,对应URL参数的timestamp
|
||||
* @param nonce 随机串,对应URL参数的nonce
|
||||
* @param echoStr 随机串,对应URL参数的echostr
|
||||
*
|
||||
* @return 解密之后的echostr
|
||||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
|
||||
*/
|
||||
public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr)
|
||||
throws AesException {
|
||||
String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);
|
||||
|
||||
if (!signature.equals(msgSignature)) {
|
||||
throw new AesException(AesException.ValidateSignatureError);
|
||||
}
|
||||
|
||||
String result = decrypt(echoStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.constant.CacheConstants;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.http.HttpUtils;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
import com.fastbee.common.wechat.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author fastb
|
||||
* @version 1.0
|
||||
* @description: 微信相关工具类
|
||||
* @date 2024-01-08 17:36
|
||||
*/
|
||||
public class WechatUtils {
|
||||
|
||||
/**
|
||||
* 网站、移动应用获取微信用户accessToken
|
||||
* @param code 用户登录code
|
||||
* @param appId 微信平台appId
|
||||
* @param secret 微信平台密钥
|
||||
* @return WeChatAppResult
|
||||
*/
|
||||
public static WeChatAppResult getAccessTokenOpenId(String code, String appId, String secret) {
|
||||
String url = FastBeeConstant.URL.WX_GET_ACCESS_TOKEN_URL_PREFIX + "?appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code";
|
||||
String s = HttpUtils.sendGet(url);
|
||||
return JSON.parseObject(s, WeChatAppResult.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信用户信息
|
||||
* @param accessToken 接口调用凭证
|
||||
* @param openId 用户唯一标识
|
||||
* @return WeChatUserInfo
|
||||
*/
|
||||
public static WeChatUserInfo getWeChatUserInfo(String accessToken, String openId) {
|
||||
String url = FastBeeConstant.URL.WX_GET_USER_INFO_URL_PREFIX + "?access_token=" + accessToken + "&openid=" + openId;
|
||||
String s = HttpUtils.sendGet(url);
|
||||
return JSON.parseObject(s, WeChatUserInfo.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序获取微信用户登录信息
|
||||
* @param code 用户凭证
|
||||
* @param appId 微信平台appId
|
||||
* @param secret 微信平台密钥
|
||||
* @return 结果
|
||||
*/
|
||||
public static WeChatMiniProgramResult codeToSession(String code, String appId, String secret) {
|
||||
String url = FastBeeConstant.URL.WX_MINI_PROGRAM_GET_USER_SESSION_URL_PREFIX + "?appid=" + appId + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
|
||||
String s = HttpUtils.sendGet(url);
|
||||
return JSON.parseObject(s, WeChatMiniProgramResult.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序获取微信用户手机号
|
||||
* @param code 凭证
|
||||
* @param accessToken 微信用户token
|
||||
* @return 手机号信息
|
||||
*/
|
||||
public static WeChatPhoneInfo getWechatUserPhoneInfo(String code, String accessToken) {
|
||||
String url = FastBeeConstant.URL.WX_GET_USER_PHONE_URL_PREFIX + accessToken;
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("code", code);
|
||||
String s = HttpUtils.sendPost(url, JSONObject.toJSONString(map));
|
||||
return JSON.parseObject(s, WeChatPhoneInfo.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序获、公众号取微信accessToken
|
||||
* @param appId 微信平台appId
|
||||
* @param secret 微信平台密钥
|
||||
* @return WeChatAppResult
|
||||
*/
|
||||
public static WeChatAppResult getAccessToken(String appId, String secret) {
|
||||
// 加个缓存
|
||||
WeChatAppResult wechatAppResultRedis = SpringUtils.getBean(RedisCache.class).getCacheObject(CacheConstants.WECHAT_GET_ACCESS_TOKEN_APPID + appId);
|
||||
if (ObjectUtil.isNotNull(wechatAppResultRedis)) {
|
||||
return wechatAppResultRedis;
|
||||
}
|
||||
String url = FastBeeConstant.URL.WX_MINI_PROGRAM_GET_ACCESS_TOKEN_URL_PREFIX + "&appid=" + appId + "&secret=" + secret;
|
||||
String s = HttpUtils.sendGet(url);
|
||||
WeChatAppResult weChatAppResult = JSON.parseObject(s, WeChatAppResult.class);
|
||||
if (ObjectUtil.isNotNull(weChatAppResult) && StringUtils.isNotEmpty(weChatAppResult.getAccessToken())) {
|
||||
SpringUtils.getBean(RedisCache.class).setCacheObject(CacheConstants.WECHAT_GET_ACCESS_TOKEN_APPID + appId, weChatAppResult, 1, TimeUnit.HOURS);
|
||||
}
|
||||
return weChatAppResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号获取微信用户信息
|
||||
* @param accessToken 接口调用凭证
|
||||
* @param openId 用户唯一标识
|
||||
* @return WeChatUserInfo
|
||||
*/
|
||||
public static WeChatUserInfo getWeChatPublicAccountUserInfo(String accessToken, String openId) {
|
||||
String url = FastBeeConstant.URL.WX_PUBLIC_ACCOUNT_GET_USER_INFO_URL_PREFIX + "?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
|
||||
String s = HttpUtils.sendGet(url);
|
||||
return JSON.parseObject(s, WeChatUserInfo.class);
|
||||
}
|
||||
|
||||
public static String responseText(WxCallBackXmlBO wxCallBackXmlBO, String content) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("<xml>");
|
||||
stringBuilder.append("<ToUserName><![CDATA[" + wxCallBackXmlBO.getFromUserName() + "]]></ToUserName>");
|
||||
stringBuilder.append("<FromUserName><![CDATA[" + wxCallBackXmlBO.getToUserName() + "]]></FromUserName>");
|
||||
stringBuilder.append("<CreateTime>" + (LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000) + "</CreateTime>");
|
||||
stringBuilder.append("<MsgType><![CDATA[text]]></MsgType>");
|
||||
stringBuilder.append("<Content><![CDATA[" + content + "]]></Content>"); //替换空格,文本信息内容不能包含有空格 .Replace(" ", string.Empty)
|
||||
stringBuilder.append("</xml>");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.fastbee.common.utils.wechat;
|
||||
|
||||
/**
|
||||
* 对企业微信发送给企业后台的消息加解密示例代码.
|
||||
*
|
||||
* @copyright Copyright (c) 1998-2014 Tencent Inc.
|
||||
*/
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* XMLParse class
|
||||
*
|
||||
* 提供提取消息格式中的密文及生成回复消息格式的接口.
|
||||
*/
|
||||
class XMLParse {
|
||||
|
||||
/**
|
||||
* 提取出xml数据包中的加密消息
|
||||
* @param xmltext 待提取的xml字符串
|
||||
* @return 提取出的加密消息字符串
|
||||
* @throws AesException
|
||||
*/
|
||||
public static Object[] extract(String xmltext) throws AesException {
|
||||
Object[] result = new Object[3];
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
|
||||
String FEATURE = null;
|
||||
// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
|
||||
// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
|
||||
FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
|
||||
dbf.setFeature(FEATURE, true);
|
||||
|
||||
// If you can't completely disable DTDs, then at least do the following:
|
||||
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
|
||||
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
|
||||
// JDK7+ - http://xml.org/sax/features/external-general-entities
|
||||
FEATURE = "http://xml.org/sax/features/external-general-entities";
|
||||
dbf.setFeature(FEATURE, false);
|
||||
|
||||
// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
|
||||
// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
|
||||
// JDK7+ - http://xml.org/sax/features/external-parameter-entities
|
||||
FEATURE = "http://xml.org/sax/features/external-parameter-entities";
|
||||
dbf.setFeature(FEATURE, false);
|
||||
|
||||
// Disable external DTDs as well
|
||||
FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
|
||||
dbf.setFeature(FEATURE, false);
|
||||
|
||||
// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
|
||||
dbf.setXIncludeAware(false);
|
||||
dbf.setExpandEntityReferences(false);
|
||||
|
||||
// And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then
|
||||
// ensure the entity settings are disabled (as shown above) and beware that SSRF attacks
|
||||
// (http://cwe.mitre.org/data/definitions/918.html) and denial
|
||||
// of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk."
|
||||
|
||||
// remaining parser logic
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
StringReader sr = new StringReader(xmltext);
|
||||
InputSource is = new InputSource(sr);
|
||||
Document document = db.parse(is);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
|
||||
result[0] = 0;
|
||||
result[1] = nodelist1.item(0).getTextContent();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new AesException(AesException.ParseXmlError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成xml消息
|
||||
* @param encrypt 加密后的消息密文
|
||||
* @param signature 安全签名
|
||||
* @param timestamp 时间戳
|
||||
* @param nonce 随机字符串
|
||||
* @return 生成的xml字符串
|
||||
*/
|
||||
public static String generate(String encrypt, String signature, String timestamp, String nonce) {
|
||||
|
||||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"
|
||||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"
|
||||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";
|
||||
return String.format(format, encrypt, signature, timestamp, nonce);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user