AOP参数校验

news2025/1/9 1:46:02

场景

在面试中,可能会问道AOP的适用场景有哪些,参数校验就是其中的一个例子

在Java使用登录or注册功能时,会需要用户提交邮箱手机号等信息,此时前端可能会做参数校验,以下代码是基于后端的参数校验,可适用于大部分场景。


功能

代码结构

在这里插入图片描述

核心的代码在GlobalOperatcionAspect类中,通过反射获取到Controller中的参数值。


@Globallnterceptor

全局注解,个人理解它可以管理其它的注解。

@Target({ElementType.METHOD})
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Mapping
public @interface Globallnterceptor {
    boolean checkParams() default false; //是否检查参数 默认不检查
}

@VerifyParam

具体的参数校验注解,只有当@Globallnterceptor中的checkParams为true时才会初步生效。因为此注解中还有required为true时才是真正开启了参数校验。

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER,ElementType.FIELD})
public @interface VerifyParam {

    int min() default -1;
    //最大长度
    int max() default -1;
    //是否必填
    boolean required() default false;
    //正则表达式校验
    VerifyRegexEnum regex() default VerifyRegexEnum.NO;
}

GlobalOperatcionAspect

在此类中:

  1. 获取到方法的参数、方法名等……
  2. 检查@Globallnterceptor注解中的checkParams是否为true
  3. 若为true,则进行参数校验,反之则不校验
  4. validateParams()方法中,检查@VerifyParam注解的required是否为true
  5. 判断参数的类型,若为基本类型,则进入checkValue()方法判断,反之进入checkObjValue()方法
@Aspect
@Component("globalOperatcionAspect")
public class GlobalOperatcionAspect {

    private static final Logger logger = LoggerFactory.getLogger(GlobalOperatcionAspect.class);
    private static final String[] TYPE_BASIS = {"java.lang.String","java.lang.Integer","java.lang.Long"
	,"java.lang.Double"};
//    private static final String TYPE_INTEGER = "java.lang.Integer";
//    private static final String TYPE_LONG = "java.lang.Long";

    @Pointcut("@annotation(com.easypan.annotation.Globallnterceptor)")
    private void requestInterceptor() {
    }

    @Before("requestInterceptor()")
    public void interceptorDo(JoinPoint point) throws BusinessException {
        try{
			//获取目标对象
            Object target = point.getTarget();
            //获取方法参数
            Object[] argements = point.getArgs();
            //获取方法名
            String methodName = point.getSignature().getName();
            //获取参数类型
            Class<?>[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
			//反射获取该方法
            Method method = target.getClass().getMethod(methodName,parameterTypes);
			//获取 method 方法上的 Globallnterceptor 注解对象。如果该方法上没有该注解,
			//则返回 null。如果有注解,则可以通过注解对象来获取注解中定义的属性值
            Globallnterceptor interceptor = method.getAnnotation(Globallnterceptor.class);
            if (interceptor == null){
                return;
            }
            /**
             * 参数校验
             */
            if(interceptor.checkParams()){
                validateParams(method,argements);
            }
			//缺少一个全局异常
        }catch (BusinessException e){
			logger.error("参数校验失败",e);
			throw new BusinessException(ResponseCodeEnum.CODE_500);
		}catch (Throwable e){
			logger.error("参数校验失败",e);
			throw new BusinessException(ResponseCodeEnum.CODE_500);
		}
	}

	/**
	 * 用于判断校验的具体类型
	 * @param method
	 * @param argements
	 */
    private void validateParams(Method method, Object[] argements)  {
        Parameter[] parameters = method.getParameters();//参数多个,所以是数组
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];// 当前方法
            Object value = argements[i]; // 当前方法调用时传入的参数值
            VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class);
            if (verifyParam == null){
                continue;
            }
			//判断当前类型是否为基本类型
			//parameter.getParameterizedType().getTypeName())反射方法,用于获取方法参数的类型名称。
            if (ArrayUtils.contains(TYPE_BASIS,parameter.getParameterizedType().getTypeName())){
				//值
				checkValue(value,verifyParam);
            }else {
                //对象
				checkObjValue(parameter,value);
            }
        }
    }

	public void checkObjValue(Parameter parameter,Object value)  {
		try {
			String typename = parameter.getParameterizedType().getTypeName();
			//获取该类型的class对象
			Class calssz = Class.forName(typename);
			//获取该对象的所有字段
			Field[] fileds = calssz.getDeclaredFields();
			for (Field field : fileds){
				VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class);
				if (fieldVerifyParam == null){
					continue;
				}
				//如果字段有非public的值,需要调用此方法,否则获取不到
				field.setAccessible(true);
				//获取当前字段的值
				Object resultValue = field.get(value);
				checkValue(resultValue,fieldVerifyParam);
			}
		}catch (BusinessException  e){
			logger.error("参数校验失败",e);
			throw e;
		} catch (Exception e) {
			logger.error("参数校验失败",e);
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
	}

    // 获取VerifyParam里面定义的属性
    private void checkValue(Object value,VerifyParam verifyParam){
		Boolean isEmpty = value==null|| StringTools.isEmpty(value.toString());
		Integer length = value==null?0:value.toString().length();
		//校验空
		if (isEmpty && verifyParam.required()){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		//校验长度
		if (!isEmpty && verifyParam.max() !=-1 && verifyParam.max()<length||verifyParam.min()!=-1&&verifyParam.min()>length){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		//校验正则
		if (!isEmpty && StringTools.isEmpty(verifyParam.regex().getRegex()) && VerifyUtils.verify(verifyParam.regex(),String.valueOf(value))){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
    }

}

StringTools

public class StringTools {

    /**
     * 判断字符串为空
     * @param str
     * @return
     */
    public static boolean isEmpty(String str){
        if (str==null || "".equals(str) || "null".equals(str) || "\u0000".equals(str)) {
            return true;
        } else return "".equals(str.trim());
    }
}

VerifyUtils:

public class VerifyUtils {

    // regex为指定的匹配规则,value为传入的待匹配校验的值
    public static boolean verify(String regex,String value){
        if(StringTools.isEmpty(value)){
            return false;
        }
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(value);
        //指示整个value字符串是否与regex模式匹配。
        return matcher.matches();
    }

    public static boolean verify(VerifyRegexEnum regex, String value){
        return verify(regex.getRegex(),value);
    }
}

VerifyRegexEnum

定义具体的匹配规则。

public enum VerifyRegexEnum {

    NO("", "不校验"),
    EMAIL("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", "邮箱"),
    PASSWORD("^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$","密码");


    private String regex;
    private String desc;



    VerifyRegexEnum(String regex, String desc) {
        this.regex = regex;
        this.desc = desc;
    }

    public String getRegex() {
        return regex;
    }

    public void setRegex(String regex) {
        this.regex = regex;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

具体方法的使用:

@RequestMapping("/sendEmailCode")
	@Globallnterceptor(checkParams = true)
    public ResponseVO sendEmailCode(HttpSession session,
									@VerifyParam(required = true,regex = VerifyRegexEnum.EMAIL) String email,
									@VerifyParam(required = true) String checkCode,
									@VerifyParam(required = true) Integer type) throws MessagingException {
		try {
			//如果前端没有传Email checkcode等,可能会报空指针异常,每个都是用if判断太麻烦,可以使用AOP参数校验
			if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))){
				throw new BusinessException("验证码错误");
			}
			emailInfoService.sendEmailCode(email,type);
			return getSuccessResponseVO(null);
		}finally {
			session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
		}
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不推荐你做网工,就因为这一个字

大家好&#xff0c;我是许公子。 我平时闲暇的时候&#xff0c;很喜欢在网上逛逛&#xff0c;看看大家最近都在聊什么。 这几天看到个问题。 网络工程师&#xff0c;累吗&#xff1f; 里面有不少回答觉得&#xff0c;网工这行&#xff0c;太累了。 “ 做实施的时候&#xf…

【学习记录22】git删除远程主分支和文件

项目分支太多&#xff0c;代码冗余太多&#xff0c;项目也没正式启用&#xff0c;想着全部清空重新来一遍&#xff0c;参考了网上的步骤都不太理想&#xff0c;全部走不通&#xff0c;搜出来大部分都是类似下面的操作。经过一段时间折腾终于删掉并清空。因此记录一下以备后期查…

小白入门SRC漏洞挖掘的正确姿势

前言 有不少阅读过我文章的伙伴都知道&#xff0c;我从事网络安全行业已经好几年&#xff0c;积累了丰富的经验和技能。在这段时间里&#xff0c;我参与了多个实际项目的规划和实施&#xff0c;成功防范了各种网络攻击和漏洞利用&#xff0c;提高了安全防护水平。 也有很多小…

Azure Services -5.25-summary

文章目录 1. Resources2.Data processing process3.Virtual network and public ip address4. Kubernetes services5. Yaml file first , we enter the homepage of microsoft azure, and we can see a lot of servicse provided by the microsoft azure , 1. Resources accou…

SSM(SpringMVC篇)

SpringMVC SpringMVC简介 概述 SpringMVC是一种基于java的实现MVC设计模型的请求驱动类型的轻量级web框架&#xff0c;属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow中 SpringMVC已经成为目前最主流的框架之一&#xff0c;并且随着Spring3.0的发布&…

Excel高手与普通人之间的差距,全在这个比Access还简单的工具

用“Excel”还是用“Python”&#xff1f; 很多职场人&#xff0c;在工作的大部分时间都会和“Excel”打交道&#xff0c;普通人&#xff0c;我们用“Excel”的录入和统计等&#xff0c;很多功能是不会用的。 只有一部分人群&#xff0c;会“Excel”的高级操作&#xff0c;比…

毫米波雷达模块在自动驾驶系统中的关键功能

随着自动驾驶技术的快速发展&#xff0c;毫米波雷达模块作为一项关键技术&#xff0c;为自动驾驶系统提供了重要的感知和决策能力。毫米波雷达模块通过实时探测和跟踪周围环境中的车辆、行人和障碍物&#xff0c;提供精确的距离和速度信息&#xff0c;帮助自动驾驶车辆做出准确…

【干货】IPV6 知识点与OSPFv3 原理与配置

今天和海翎光电的小编咱们一起聊聊IPv6的报头。下图是IPV4和IPV6的对比图&#xff0c;个人感觉还是比较直观的&#xff0c;我们就以这张图来梳理IPv6的报头内容。 一、IPv4的报头内容 首先&#xff0c;我们先重新回顾一下IPv4的报头内容&#xff0c;对于这个内容比较熟悉的朋友…

第十二章 异常(Exception)

一、异常的概念&#xff08;P444&#xff09; Java 语言中&#xff0c;将程序执行中发生的不正常情况称为“异常”。&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09; 执行过程中所发生的异常事件可分为两大类&#xff1a; &#xff08;1&#xff09;Error&…

Three.js--》实现3d圣诞贺卡展示模型

目录 项目搭建 初始化three.js基础代码 加载环境模型 设置环境纹理 添加水面并设置阴影效果 实现幽灵小球的运动 实现相机切换和文字切屏 实现漫天星星和爱心样式 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目…

QT 一个project调用另外一个project

1.随便建两个project, test888 (test888 里面随便写一个类如CHPerson), test999 2.把test888放在test999目录下 3.在test888里面手动创建一个test888.pri文件&#xff0c;里面内容如下: INCLUDEPATH $$PWDHEADERS \$$PWD/CHPerson.h \SOURCES \$$PWD/CHPerson.cpp \4.在tes…

集权设施攻防兵法:实战攻防之Exchange篇

一、黑客视角下的Exchange Microsoft Exchange Server是由微软开发的企业级邮件和协作平台。它提供了强大的电子邮件、日历、联系人和任务管理功能&#xff0c;使组织能够高效地进行沟通和协作。然而&#xff0c;它也时常受到攻击。 攻击者喜欢攻击Exchange服务器有两个重要原…

基于遗传算法的LQR控制器优化设计(matlab实现)

以下内容大部分来源于《MATLAB智能算法30个案例分析》&#xff0c;仅为学习交流所用。 5.1 理论基础 5.1.1 LQR控制 假设线性时不变系统的状态方程模型为 可以引入最优控制的性能指标&#xff0c;即设计一个输入量u,使得 为最小。其中&#xff0c;Q和R分别为状态变量和输…

JavaWeb ( 十 ) SpringMVC

4.Spring MVC Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。 三层架构分为表述层&#xff08;或表示层)、业务逻辑层、数据访问层&#xff0c;表述层表示前台页面和后台servlet 4.1.Spring MVC优点&#xff1a; ① 基于原生的Servlet&#xff0c;通过…

10 款最常用的Sketch在线插件!

Sketch 是一款高效、小巧的界面设计工具&#xff0c;在设计领域广受设计团队喜爱&#xff0c;帮助设计师创造了许多令人惊叹的作品。在使用 Sketch 时&#xff0c;辅助使用一些插件可以更高效地完成设计任务。Windows 也能用的「协作版 Sketch」即时设计&#xff0c;可作为网页…

三、Go的常用命令以及Go的执行原理

Go的执行原理以及Go的命令 一、Go的源码文件 Go 的源码文件分类&#xff1a; 如上图&#xff0c;分为三类&#xff1a; 1、命令源码文件&#xff1a; 声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。 命令源码文件被安装以后&#xff0c;GOPATH 如果…

1032 Sharing(35行代码+超详细注释+测试点4分析)

分数 25 全屏浏览题目 切换布局 作者 CHEN, Yue 单位 浙江大学 To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For …

弘博创新2023读书会|“读”赢成长,“书”立未来

读万卷书&#xff0c;行万里路&#xff0c;以书会友&#xff0c;提升自我。 为了让大家在繁忙的工作中抽时间静下心来读书&#xff0c;与志同道合的人交流和分享自己的想法&#xff0c;弘博创新于5月21日举办了线下读书会活动&#xff0c;学友们都积极参加本次读书会。 参加读书…

JUC-JMM模型、CAS、AQS

JMM&#xff08;Java内存模型&#xff09;&#xff0c;主要描述了一组规则&#xff0c;主要定义了程序执行过程中变量的访问方式来保证单线程、多线程下的正常执行。JVM运行的实体是线程&#xff0c;每个线程运行时&#xff0c;都会创建一个工作内存【栈空间&#xff08;栈帧&a…

接口测试的测试要点

接口测试的测试要点&#xff0c;你知道都有哪些吗&#xff1f; 接口测试是软件测试中的重要组成部分&#xff0c;它的目的是评估接口的质量和可靠性&#xff0c;以保证系统的正常运行。在进行接口测试时&#xff0c;必须要考虑到以下几个方面&#xff1a; 测试用例的编写 测试…