AOP+责任链

该博客介绍了如何使用自定义注解`@RequestAdvice`进行安全检查和请求处理。定义了`RequestAdvice`注解,用于标记需要进行安全检查的方法,并指定处理类。`RequestAdviceHandler`接口定义了拦截器方法,包括`preHandle`、`postHandle`和`afterCompletion`。此外,还实现了多个处理类,如`CommonRequestAdviceHandler`和`SecurityCheckAdviceHandler`,分别用于通用请求记录和安全检查。整个流程通过AOP实现,确保请求在执行前后进行相应的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 自定义注解进行安全检查
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;
    }


}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值