AOP实现对controller入参校验

之前leader交给我一个校验controller入参的任务,最终代码不太符合要求,被废弃,特此把代码记录下来,便于以后整理,提高。

先讲一下怎么用,目前版本只需要在需要校验的controller方法上添加@Validate注解,处理类中会自动识别String和bean,bean类型,需要在对应字段上添加@Require,才会对字段进行非空校验。

废话不多说,先贴代码(先写个初版,待有时间整理一下这篇博客):

配置信息:(以@Validate为切点)

    <bean id="validateAdvitor" class="com.zw.web.base.validators.ParamValidateAdvisor"/>
    <aop:config proxy-target-class="true">
        <aop:aspect ref="validateAdvitor">
            <aop:pointcut expression="@annotation(com.zw.web.base.validators.annos.Validate)" id="validateCut"/>
            <aop:around method="validate" pointcut-ref="validateCut"/>
        </aop:aspect>
    </aop:config>

自定义注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 *
 *自定义校验注解
 * @author WJ_wa
 * @create 2018/8/21
 * @since 1.0.0
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {
    public boolean isValidate() default true;
    public boolean isBean() default false;
    public boolean isRequest() default false;
}
import org.springframework.beans.factory.annotation.Required;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * bean实体中的〈必须项〉
 * @author WJ_wa
 * @create 2018/8/21
 * @since 1.0.0
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface  Require {
    public boolean isRequired() default true;

}

方法参数类:

import java.lang.annotation.Annotation;
/**
 * 〈方法参数类
 * @author WJ_wa
 * @create 2018/8/21
 * @since 1.0.0
 */
public class Param {
    /**
     * 名字
     */
    private String name;
    /**
     * 类型
     */
    private Class<?> type;
    /**
     * 值
     */
    private Object value;
    /**
     * 注解
     */
    private Annotation anno;

    public Param() {
        super();
    }

    public Param(String name, Class<?> type, Object value, Annotation anno) {
        super();
        this.name = name;
        this.type = type;
        this.value = value;
        this.anno = anno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public Annotation getAnno() {
        return anno;
    }

    public void setAnno(Annotation anno) {
        this.anno = anno;
    }

    @Override
    public String toString() {
        return "Param [name=" + name + ", type=" + type + ", value=" + value + ", anno=" + anno + "]";
    }
}

注解帮助类:

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.zw.web.base.validators.annos.Validate;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import javax.servlet.http.HttpServletRequest;


/**
 * 注解帮助类
 * @author WJ_wa
 * @create 2018/8/21
 * @since 1.0.0
 */
public class AnnotationHelper {
    /**
     * 获取MethodSignature
     * @param point
     * @return
     */
    public static Signature getMethod(ProceedingJoinPoint point){
        MethodSignature sign = (MethodSignature) point.getSignature();
        return sign;
    }

    /**
     * 获取参数列表
     * @param point
     * @return
     */
    public static Object[] getArgs(ProceedingJoinPoint point){
        return point.getArgs();
    }

    /**
     * 获取参数的描述(加Validate注解的)
     * @param method
     * @param objs
     * @return
     */
    public static List<Param> getParms(Method method, Object[] objs) throws Exception{
        Annotation annotation = null;
        Annotation[][] annos = method.getParameterAnnotations();
        Annotation[] annosMethod = method.getAnnotations();
        for(Annotation ann : annosMethod){
            if(ann.annotationType() == Validate.class){
                annotation = ann;
                break;
            }
        }
        Class<?>[] paramTypes = method.getParameterTypes();
        List<Param> params = new ArrayList<Param>();
        for(int i=0;i<annos.length;i++){
            boolean flag = true;
            for(int j=0;j<annos[i].length;j++){
                //如果出现指定的注解类型
                if(annos[i][j].annotationType() == Validate.class){
                    Param param = new Param(paramTypes[i].getName(),
                            paramTypes[i],
                            objs[i],
                            annos[i][j]);
                    params.add(param);
                    flag = false;
                }
            }
            //参数上无注解,主动加上注解,后面判断逻辑需要
            if(flag){
                String name = paramTypes[i].getName();
                //此处判断是bean类型的
                if(name != String.class.getName() && name != HttpServletRequest.class.getName()){
                    //获取 annotation 这个代理实例所持有的 InvocationHandler
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
                    // 获取 AnnotationInvocationHandler 的 memberValues 字段
                    Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues");
                    // 因为这个字段是 private final 修饰,所以要打开权限
                    declaredField.setAccessible(true);
                    // 获取 memberValues
                    Map memberValues = (Map) declaredField.get(invocationHandler);
                    // 修改 value 属性值
                    memberValues.put("isBean", true);
                    Param param = new Param(paramTypes[i].getName(),
                            paramTypes[i],
                            objs[i],
                            annotation);
                    params.add(param);
                    continue;
                }
                //判断是request类型的,暂时无用,目前不处理request
                if(name == HttpServletRequest.class.getName()){
                    //获取 annotation 这个代理实例所持有的 InvocationHandler
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
                    // 获取 AnnotationInvocationHandler 的 memberValues 字段
                    Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues");
                    // 因为这个字段是 private final 修饰,所以要打开权限
                    declaredField.setAccessible(true);
                    // 获取 memberValues
                    Map memberValues = (Map) declaredField.get(invocationHandler);
                    // 修改 value 属性值
                    memberValues.put("isRequest", true);
                    Param param = new Param(paramTypes[i].getName(),
                            paramTypes[i],
                            objs[i],
                            annotation);
                    params.add(param);
                    continue;
                }
            }
        }
        return params;
    }
}

切面实现:

import com.base.util.TraceLoggerUtil;
import com.zw.util.ChkUtil;
import com.zw.web.base.validators.annos.Require;
import com.zw.web.base.validators.annos.Validate;
import com.zw.web.base.vo.ResultVO;
import com.zw.web.base.vo.VOConst;
import org.aspectj.lang.ProceedingJoinPoint;

import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;


/**
 * 〈参数验证〉
 *
 * @author WJ_wa
 * @create 2018/8/21
 * @since 1.0.0
 */
public class ParamValidateAdvisor{

    /**
     * 校验入参
     * @param point
     * @throws Throwable
     */
    public Object validate(ProceedingJoinPoint point) throws Throwable{
        TraceLoggerUtil.info("开始拦截接口入参!");
        //获取切点处的所有入参
        Object[] objs = point.getArgs();
        //获取response
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        //获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取方法
        Method method = signature.getMethod();
        /**
         * 下面代码是校验参数是否有@Validate注解,现在废弃掉;需求:方法上添加@Validate注解即校验所有入参,有不想校验的,在对应参数前添加@Validate(isValidate=false)
         */
//        //获取入参的注解
//        Annotation[][] annosParam = method.getParameterAnnotations();
//        //校验
//        boolean paramFlag = validateParameterAnnotation(annosParam);
//        //虽然方法加了注解,但是参数么有注解,pass
//        if(!paramFlag){
//            return point.proceed(objs);
//        }
        List<Param> params = AnnotationHelper.getParms(method,objs);
        if(!params.isEmpty()){
            for(Param param : params){
                boolean isSuccess = validateDetail(param);
                if(!isSuccess){
                    TraceLoggerUtil.error("入参校验失败!param{}",param);
                    try {
                        PrintWriter out = response.getWriter();
                        ResultVO resultVO = new ResultVO();
                        resultVO.setErrorMsg(VOConst.FAIL, "系统繁忙,请您稍后尝试,如多次尝试失败,请联系客服。");
                        out.write(resultVO.toString());
                        out.flush();
                        return null;
                    }catch (Exception e){
                        TraceLoggerUtil.error("输出入参校验失败信息到response失败!");
                    }
                }
            }
        }
        TraceLoggerUtil.info("接口入参校验结束,全部通过!");
        //没有错误就继续!
        return point.proceed(objs);
    }

    /**
     * 验证是否有某个注解,带有@Validate注解的才会校验
     * @param annosParam
     * @param
     * @return
     */
    private boolean validateParameterAnnotation(Annotation[][] annosParam){
        boolean flag = false;
        for(Annotation[] at : annosParam){
            for(Annotation a : at){
                if(a.annotationType() == Validate.class){
                    flag = true;
                }
            }
        }
        return flag;
    }

    /**
     * 具体的校验逻辑
     * @param
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    private boolean validateDetail(Param param) throws IllegalArgumentException, IllegalAccessException{
        Validate val = (Validate)param.getAnno();
        boolean isVali = val.isValidate();
        boolean result = false;
        if(isVali){
            if(val.isBean() == true){
                result= validateForm(param);
            }else if(val.isRequest() == true){
                //校验request的未用,暂留,里面未写逻辑代码
                result= validateRequest(param);
            }else{
                result = validateCommon(param);
            }
        }
        return result;
    }

    /**
     * 验证非bean类型、非HttpServletRequest类型参数是否为空
     * @param param
     * @return
     */
    private boolean  validateCommon(Param param){
        boolean res = false;
        if(ChkUtil.isNotEmpty(param.getValue())){
            res = true;
        }
        return res;
    }

    /**
     * 验证HttpServletRequest类型参数是否为空,暂时不可用,待拓展
     * @param param
     * @return
     */
    private boolean  validateRequest(Param param){
       return true;
    }

    /**
     * 验证bean类型参数是否为空
     * @param param
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    private boolean validateForm(Param param) throws IllegalArgumentException, IllegalAccessException{
        Class<?> clazz = param.getValue().getClass();
        Field[] fields = clazz.getDeclaredFields();
        for(Field f : fields){
            Annotation[] annos = f.getAnnotations();
            if(annos!=null&&annos.length>0){
                Require require = (Require)annos[0];
                if(require.isRequired()){
                    //允许访问私有变量
                    f.setAccessible(true);
                    Object obj = f.get(param.getValue());
                    Class<?> type = f.getType();
                    //鉴于目前bean类中字段类型全部为String,此处只校验String类型,其他类型数据请自行拓展
                    if(type == String.class){
                        if(ChkUtil.isEmpty(obj)){
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/w1171155814/article/details/82192055