- 自定义注解进行安全检查
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @description: 安全检查
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestAdvice {
boolean checkSign() default true;
/**
* 请求记录处理class
*
* @return
*/
Class<? extends RequestAdviceHandler>[] adviceHandlerClasses() default {CommonRequestAdviceHandler.class};
}
-
@RequestAdvice(adviceHandlerClasses = {TemplateInvokeAdviceHandler.class})
-
定义拦截器接口
import org.aspectj.lang.Signature;
/**
* @description: 请求数据记录 拦截器
*/
public interface RequestAdviceHandler {
boolean preHandle(Object object , Object[] paramValues, Signature signature,RequestAdvice requestAdvice) ;
void postHandle(Object object , Object[] paramValues, Signature signature);
void afterCompletion(Object object , Object[] paramValues, Signature signature, Throwable t) ;
}
- 定义拦截器接口实现类(多个)
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/**
* @description: 统一的请求记录
*/
@Component
public class CommonRequestAdviceHandler implements RequestAdviceHandler {
@Override
public boolean preHandle(Object object, Object[] paramValues, Signature signature, RequestAdvice requestAdvice) {
return true;
}
@Override
public void postHandle(Object object, Object[] paramValues, Signature signature) {
//implement in the future
}
@Override
public void afterCompletion(Object object, Object[] paramValues, Signature signature, Throwable t) {
//implement in the future
}
}
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
/**
* @description: 安全检查handler
*/
@Slf4j
@Component
public class SecurityCheckAdviceHandler implements RequestAdviceHandler {
@Resource
private SignParameterParser signParameterParser;
@Resource
private SecurityChecker securityChecker;
@Override
public boolean preHandle(Object object, Object[] paramValues, Signature signature, RequestAdvice requestAdvice) {
String methodName = signature.getName();
String[] paramNames = ((CodeSignature) signature).getParameterNames();
//将mvc解析的参数加入签名map
Map<String, String> signatureMap = signParameterParser.getParamMap(paramNames, paramValues);
//为了避免在springMvc controller中额外定义签名参数,所以直接从request中获取
boolean check = signParameterParser.collectAndCheckSignatureParams(signatureMap);
//如果跳过检查则不进行签名检查
if (check) {
String sign = signatureMap.get(SignConstant.SIGNATURE);
signatureMap.remove(SignConstant.SIGNATURE);
return securityChecker.doCheck(methodName, signatureMap.get(SignConstant.APP_KEY), sign, signatureMap);
}
return true;
}
@Override
public void postHandle(Object object, Object[] paramValues, Signature signature) {
// do nothing
}
@Override
public void afterCompletion(Object object, Object[] paramValues, Signature signature, Throwable t) {
// do nothing
}
}
import com.google.common.collect.Maps;
import common.exception.ServiceException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* @description: 参数解析
*/
@Component
public class SignParameterParser {
/**
* 判断object是否为基本类型
*
* @param object
* @return
*/
public static boolean isBaseType(@NotNull Object object) {
Class<?> className = object.getClass();
return className.equals(java.lang.Integer.class) ||
className.equals(java.lang.Byte.class) ||
className.equals(java.lang.Long.class) ||
className.equals(java.lang.Double.class) ||
className.equals(java.lang.Float.class) ||
className.equals(java.lang.Character.class) ||
className.equals(java.lang.Short.class) ||
className.equals(java.lang.Boolean.class) ||
className.equals(java.lang.String.class);
}
/**
* 收集并校验参数
*
* @param signatureMap
* @return 是否需要验签
*/
public boolean collectAndCheckSignatureParams(Map<String, String> signatureMap) {
String appKey = getParamFromRequest(SignConstant.APP_KEY);
String signature = getParamFromRequest(SignConstant.SIGNATURE);
String expires = getParamFromRequest(SignConstant.EXPIRES);
String nonce = getParamFromRequest(SignConstant.NONCE);
signatureMap.put(SignConstant.APP_KEY, appKey);
signatureMap.put(SignConstant.EXPIRES, expires);
signatureMap.put(SignConstant.NONCE, nonce);
signatureMap.put(SignConstant.SIGNATURE, signature);
if (SignConstant.UN_CHECK_APP_KEY.equals(appKey)) {
//放开检查
return false;
}
if (StringUtils.isEmpty(appKey) ||
StringUtils.isEmpty(signature) ||
StringUtils.isEmpty(expires)) {
throw new ServiceException(ErrorMessage.SING_NOT_EXIST.getCode(), ErrorMessage.SING_NOT_EXIST.getValue());
}
Long outTimeStamp = Long.valueOf(expires);
if (outTimeStamp < System.currentTimeMillis() / Conversion.RATIO_THOUSAND) {
throw new ServiceException(ErrorMessage.SING_TIME_OUT.getCode(), ErrorMessage.SING_TIME_OUT.getValue());
}
return true;
}
/**
* 从原始的request获取签名参数
*
* @param paramName
* @return
*/
private String getParamFromRequest(String paramName) {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
if (Objects.isNull(ra)) {
return StringUtils.EMPTY;
}
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
return request.getParameter(paramName);
}
/**
* 将Object对象里面的属性和值转化成Map对象
*
* @param obj
* @return
* @throws IllegalAccessException
*/
private Map<String, String> objectToMap(Object obj) {
if (obj instanceof Map) {
Map<String, String> returnMap = Maps.newHashMap();
Set<Map.Entry> entrySet = ((Map) obj).entrySet();
entrySet.forEach(entry -> returnMap.put(String.valueOf(entry.getKey())
, String.valueOf(entry.getValue())));
return returnMap;
}
Map<String, String> map = Maps.newHashMap();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
//通过getter方法获取value,暂时不支持 isXXX
Object value = ReflectionOperatorUtils.getFieldValue(field.getName(), obj);
//只对String的类型的进行签名计算
if (Objects.nonNull(value) && isBaseType(value)) {
map.put(field.getName(), String.valueOf(value));
}
}
return map;
}
/**
* 将所有的参数转化为map形式
*
* @param paramNames
* @param paramValue
* @return
*/
public Map<String, String> getParamMap(String[] paramNames, Object[] paramValue) {
Map<String, String> map = Maps.newHashMap();
if (ArrayUtils.isNotEmpty(paramNames) && ArrayUtils.isNotEmpty(paramValue)) {
for (int i = 0; i < paramValue.length; i++) {
if (Objects.nonNull(paramValue[i])) {
if (isBaseType(paramValue[i])) {
map.put(paramNames[i], String.valueOf(paramValue[i]));
} else {
map.putAll(objectToMap(paramValue[i]));
}
}
}
}
return map;
}
}
/**
* @description: 签名的常量
*/
public class SignConstant {
public static final String APP_KEY = "appkey";
public static final String EXPIRES = "expires";
public static final String SIGNATURE = "signature";
public static final String NONCE = "nonce";
private SignConstant() {
}
}
import com.google.common.base.Preconditions;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Objects;
public class ReflectionOperatorUtils {
private ReflectionOperatorUtils() {
}
public static Object getFieldValue(String fieldName, Object object) {
Preconditions.checkArgument(!StringUtils.isEmpty(fieldName), "fieldName错误!");
Method method = getGetterMethod(fieldName, object.getClass());
if (Objects.nonNull(method)) {
return ReflectionUtils.invokeMethod(method, object);
}
return null;
}
/**
* haveGetter
*
* @param fieldName
* @param clazz
* @return
*/
public static Method getGetterMethod(String fieldName, Class clazz) {
Preconditions.checkArgument(!StringUtils.isEmpty(fieldName), "fieldName错误!");
String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length());
return ReflectionUtils.findMethod(clazz, methodName);
}
}
- 定义拦截链
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.Signature;
import java.util.List;
/**
* @description: 通知处理链
*/
@Slf4j
public class AdviceHandlerExecutionChain {
private List<RequestAdviceHandler> requestAdviceHandlerList = Lists.newArrayList();
/**
* 加入的顺序即为执行的顺序
* 如果已经存在了handler,则不做任何处理
*
* @param requestAdviceHandler
*/
public void add(RequestAdviceHandler requestAdviceHandler) {
if (!requestAdviceHandlerList.contains(requestAdviceHandler)) {
//加入的顺序即为执行的顺序
requestAdviceHandlerList.add(requestAdviceHandler);
}
}
boolean applyPreHandle(Object object, Object[] paramValues, Signature signature, RequestAdvice requestAdvice) {
int size = requestAdviceHandlerList.size();
for (int i = 0; i < size; i++) {
RequestAdviceHandler requestAdviceHandler = requestAdviceHandlerList.get(i);
if (!requestAdviceHandler.preHandle(object, paramValues, signature, requestAdvice)) {
return false;
}
}
return true;
}
void applyPostHandle(Object object, Object[] paramValues, Signature signature) {
int size = requestAdviceHandlerList.size();
for (int i = 0; i < size; i++) {
RequestAdviceHandler requestAdviceHandler = requestAdviceHandlerList.get(i);
requestAdviceHandler.postHandle(object, paramValues, signature);
}
}
void triggerAfterCompletion(Object object, Object[] paramValues, Signature signature, Throwable t) {
int size = requestAdviceHandlerList.size();
for (int i = 0; i < size; i++) {
RequestAdviceHandler requestAdviceHandler = requestAdviceHandlerList.get(i);
try {
requestAdviceHandler.afterCompletion(object, paramValues, signature, t);
} catch (Exception e) {
log.error("afterCompletion 执行出现异常", e);
}
}
}
}
- Aop调用
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
@Slf4j
@Order(100)
@Aspect
@Component
public class RequestAdviceHandlerAspect {
@Resource
private ApplicationContext applicationContext;
@Around(value = "checkSignPointcut()")
public Object doServiceImplPointAround(ProceedingJoinPoint joinPoint) throws Throwable {
Throwable t = null;
Object object = null;
//被注解的方法参数
Object[] paramValues = null;
//被注解方法的签名
MethodSignature methodSignature = null;
AdviceHandlerExecutionChain executionChain = null;
try {
object = joinPoint.getTarget();
methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
paramValues = joinPoint.getArgs();
RequestAdvice requestAdvice = method.getAnnotation(RequestAdvice.class);
//构建执行链
executionChain = buildAdviceHandlerExecutionChain(requestAdvice);
//请求前处理
boolean res = executionChain.applyPreHandle(object, paramValues, methodSignature, requestAdvice);
if (res) {
Object result = joinPoint.proceed();
//请求正常完成式
executionChain.applyPostHandle(joinPoint.getTarget(), paramValues, joinPoint.getSignature());
return result;
}
return null;
} catch (Throwable t1) {
t = t1;
throw t1;
} finally {
if (Objects.nonNull(executionChain)) {
executionChain.triggerAfterCompletion(object, paramValues, methodSignature, t);
}
}
}
@Pointcut(value = "@annotation(*.*.*.*.*.RequestAdvice) ") //copy reference
public void checkSignPointcut() {
// 切点的方法
}
/**
* 构建执行链
*
* @param securityCheck
* @return
*/
private AdviceHandlerExecutionChain buildAdviceHandlerExecutionChain(RequestAdvice securityCheck) {
Class<? extends RequestAdviceHandler>[] adviceHandlerClassArr = securityCheck.adviceHandlerClasses();
AdviceHandlerExecutionChain chain = new AdviceHandlerExecutionChain();
//如果需要签名,自动加入签名 handler
if (securityCheck.checkSign()) {
chain.add(applicationContext.getBean(SecurityCheckAdviceHandler.class));
}
Arrays.stream(adviceHandlerClassArr).forEach(beanClass -> chain.add(applicationContext.getBean(beanClass)));
return chain;
}
}