可在切面代码修改为数据库获取或redis中获取
依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
自定义注解
package com.ekkcole.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/***样例数据 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiCommon {
}
AOP切面代码
package com.ekkcole.aspects;
import cn.hutool.core.date.DateUtil;
import com.ekkcole.utils.CommUtils;
import com.ekkcole.utils.CommonDateUtil;
import com.ekkcole.utils.MD5Utils;
import lombok.AllArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.Date;
import static java.time.LocalDate.now;
/**
* 对使用ApiCommon注解实现切面
* @author 曹猛
*/
@Aspect
@Component
@AllArgsConstructor
public class ApiSignAspectSection {
private static final String SIGN_NAME = "signName";
private static final String ACCESS_KEY = "accessKey";
private static final String DATA_TAMP = "datatamp";
private static final String SECURITY_KEY = "securityKey";
private static final String NEW_SIGN_NAME = "AJGOJEW098823gajgjoa8u982GOAJOG";
private static final String NEW_ACCESS_KEY = "ALGNJHO883989GS;Z;KJIGJIDDS883LSJLA";
private static final Integer LENGTH = 13;
/**
* 切面,只对使用了ApiCommon注解的接口生效
*/
@Pointcut(value = "@annotation(com.ekkcole.annotation.ApiCommon)")
public void cutPoint() {
}
@Around("cutPoint()")
public Object arround(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 签名
String signName = null;
// 秘钥
String accessKey = null;
// 时间戳
String datatamp = null;
// 安全秘钥加密后由signName-accessKey 经过MD5加密后得到
String securityKey = null;
if (!CommUtils.isEmpty(request.getHeader(SIGN_NAME))) {
signName = request.getHeader(SIGN_NAME);
}
if (!CommUtils.isEmpty(request.getHeader(ACCESS_KEY))) {
accessKey = request.getHeader(ACCESS_KEY);
}
if (!CommUtils.isEmpty(request.getHeader(DATA_TAMP))) {
datatamp = request.getHeader(DATA_TAMP);
}
if (!CommUtils.isEmpty(request.getHeader(SECURITY_KEY))) {
securityKey = request.getHeader(SECURITY_KEY);
}
if (CommUtils.isEmpty(signName) || CommUtils.isEmpty(accessKey)) {
throw new RuntimeException("签名或秘钥不可为空");
}
if (!NEW_SIGN_NAME.equals(signName) && !NEW_ACCESS_KEY.equals(accessKey)) {
throw new RuntimeException("验证失败,请检查签名或秘钥是否正确");
}
// 传入的签名与秘钥拼接与安全密钥加密验证
String md5TextKey = signName + "-" + accessKey;
// 判断安全秘钥加密是否为空
if (CommUtils.isEmpty(securityKey)) {
throw new RuntimeException("安全密钥不可为空");
}
// 验证加密秘钥是否正确
if (!MD5Utils.valid(md5TextKey, securityKey)) {
throw new RuntimeException("安全密钥验证失败");
}
if (CommUtils.isEmpty(datatamp)) {
throw new RuntimeException("时间戳不可为空");
}
if (datatamp.length() != LENGTH) {
throw new RuntimeException("时间戳错误,请传入毫秒级的时间戳");
}
//验证时间戳是否在有效的范围内
Date time = CommonDateUtil.stampToDate(datatamp);
//计算日期差,传入的时间与当前时间相比较,获取差值
long diff = Math.abs(CommonDateUtil.diff(new Date(), time, CommonDateUtil.MINUTE_MS));
if (diff < 0 || diff > 5) {
throw new RuntimeException("已过期,请更新时间戳");
}
return pjp.proceed();
}
}
工具类
MD5工具
package com.ekkcole.utils;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5加密/验证工具类
*/
public class MD5Utils {
static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static final char hexDigitsLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 对字符串 MD5 无盐值加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
// digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值。1 固定值
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
// 获得密文
byte[] mdResult = md.digest();
// 把密文转换成十六进制的字符串形式
int j = mdResult.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = mdResult[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];// 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText, String saltValue) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
md.update(saltValue.getBytes());
// digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值。1 固定值
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText, String saltValue) {
try {
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
md.update(plainText.getBytes());
md.update(saltValue.getBytes());
// 获得密文
byte[] mdResult = md.digest();
// 把密文转换成十六进制的字符串形式
int j = mdResult.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = mdResult[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* MD5加密后生成32位(小写字母+数字)字符串
* 同 MD5Lower() 一样
*/
public final static String MD5(String plainText) {
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(plainText.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigitsLower[byte0 >>> 4 & 0xf];
str[k++] = hexDigitsLower[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
/**
* 校验MD5码
*
* @param text 要校验的字符串
* @param md5 md5值
* @return 校验结果
*/
public static boolean valid(String text, String md5) {
return md5.equals(MD5(text)) || md5.equals(MD5(text).toUpperCase());
}
}
时间工具
package com.ekkcole.utils;
import cn.hutool.core.date.DateUtil;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
/**
* 通用工具类
*
* @author Chill
*/
public class CommonDateUtil {
private final static String FIRST="1";
private final static String SECOND="2";
private final static String THIRD="3";
private final static String FOUR="4";
/** 毫秒 */
private final static long MS = 1;
/** 每秒钟的毫秒数 */
private final static long SECOND_MS = MS * 1000;
/** 每分钟的毫秒数 */
public final static long MINUTE_MS = SECOND_MS * 60;
/**
* 字符串转换数字
*
* @param id
* @return
*/
public static Integer parseInt(String id) {
Integer uId = 0;
if (id == null || "".equals(id))
return -1;
try {
uId = Integer.parseInt(id);
} catch (NumberFormatException e) {
// e.printStackTrace();
uId = -1;
}
return uId;
}
/**
* LocalDateTime 转换为 Date
*
* @param localDateTime
* @return
*/
public static Date LocalDateTimeToDate(LocalDateTime localDateTime) {
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
return Date.from(instant);
}
/**
* Date 转换为 LocalDateTime
*
* @param date
* @return
*/
public static LocalDateTime DateToLocalDateTime(Date date) {
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
}
/**
* 解析字符串格式的时间
* @param date
* @return
*/
public static Map<String, Object> parseDateStr(String date) {
Objects.requireNonNull(date);
Map<String, Object> dateMap = new HashMap<>();
String[] values = date.split(",");
if (values != null && values.length == 2) {
String begin = values[0].trim();
if (begin != null && begin.length() >= 10)
begin = begin.substring(0, 10);
dateMap.put("begin", begin);
String end = values[1].trim();
if (end != null && end.length() >= 10)
end = end.substring(0, 10);
dateMap.put("end", end);
}
return dateMap;
}
/**
* 获取对象的所有属性,包括从父类继承的属性
*
* @param obj
* @param parent
* @return
*/
public static Field[] getFields(Object obj, boolean parent) {
Objects.requireNonNull(obj);
List<Field> fieldList = new ArrayList<>();
Class<? extends Object> clazz = obj.getClass();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = parent ? clazz.getSuperclass() : null;
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}
/**
* 验证字符串是否为空
*
* @param value
* @return
*/
public static boolean notEmpty(String value) {
return value != null && !"".equals(value);
}
/**
* 首字母转换为大写
*
* @param fieldName
* @return
*/
public static String capitalize(String fieldName) {
String getName = "";
if (notEmpty(fieldName)) {
getName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
return getName;
}
public static Date strToDate(String dateStr, String format) {
Date date = new Date();
if (dateStr != null) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
date = sdf.parse(dateStr);
} catch (ParseException e) {
date = new Date();
}
}
return date;
}
public static String parseDateStr(Date date, String format) {
String dateStr = "";
if (date != null) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
dateStr = sdf.format(date);
}
return dateStr;
}
/**
* 计算日期差
* @param subtrahend
* @param minuend
* @param diffField
* @return
*/
public static long diff(Date subtrahend, Date minuend, long diffField) {
long diff = minuend.getTime() - subtrahend.getTime();
return diff / diffField;
}
/**
* 将时间戳变为日期
* @param s
* @return
*/
public static Date stampToDate(String s){
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long lt = new Long(s);
Date date = new Date(lt);
res = simpleDateFormat.format(date);
return DateUtil.parse(res, "yyyy-MM-dd HH:mm:ss");
}
}
测试