前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >发送邮箱验证码时怎么用AOP?没有理论,纯实战!

发送邮箱验证码时怎么用AOP?没有理论,纯实战!

原创
作者头像
xiao李
发布于 2024-07-27 14:11:39
发布于 2024-07-27 14:11:39
1130
举报

AOP能做什么?答:参数的校验 怎么实现AOP呢?答:就写一个注解,在相应的方法上加注解…… 怎么实现AOP呢?

1.导包

代码语言:xml
AI代码解释
复制
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

版本根据springboot的版本自己决定,我用的boot2.6.1,所以aop就用的1.9.4

这个包不是spring的,也不是jdk的

实现切面有很多中方式,最常见的就是定义一个注解

2.定义注解

先来看这样两个方法:

代码语言:java
AI代码解释
复制
    public void checkCode(HttpServletResponse response, HttpSession session, Integer type) throws IOException {
        CreateImageCode vCode = new CreateImageCode(130, 38, 5, 10);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");

        String code = vCode.getCode();

        if (type == null || type == 0) {
            session.setAttribute(Constants.CHECK_CODE_KEY, code);
        } else {
            session.setAttribute(Constants.CHECK_CODE_KEY_EMAIL, code);
        }
        vCode.write(response.getOutputStream());
    }

代码语言:java
AI代码解释
复制
public ResponseVO sendEmailCode(HttpSession session, String email, String checkCode, Integer type) {
        try {
            //解决空指针异常,需要判断
            //使用AOP做参数校验
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))) {
                throw new BusinessException("图片验证码不正确");
            }
            emailCodeService.sendEmailCode(email, type);
            return getSuccessResponseVO(null);
        } finally {
            //每次用完这个验证码,不管成功或者失败,都要重置
            session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
        }

    }

很显然,response和session不为空,可能传null的只有type,而方法中对type设置了默认值。所以,checkCode方法不需要参数校验。

相反,sendEmailCode方法中没有默认值就必须进行校验。

所以需要定义一个注解进行参数校验

代码语言:java
AI代码解释
复制
@Target({ElementType.METHOD}) //定义在方法上
@Retention(RetentionPolicy.RUNTIME) //在运行时保留
@Documented
@Mapping
public @interface GlobalInterceptor {
    /**
     * 校验参数
     * 默认不校验参数
     * @return
     */
    boolean checkParams() default false;
}
  • 因为要在方法上使用,所以要加注解@Target({ElementType.METHOD})
  • @Retention是一个Java元注解,他的官方解释是:用于指定另一个注解的保留策略。元注解是用于注解其他注解的特殊注解。
    • 我们只要知道他是干什么的就行了。通俗理解就是让我们定义的这个注解什么时候保留,保留策略一共有三RetentionPolicy.SOURCE:注解只在源码中保留,编译时会被丢弃,不会保留在 .class 文件中。RetentionPolicy.CLASS:注解在编译时保留在 .class 文件中,但在运行时不可见。默认保留策略。RetentionPolicy.RUNTIME:注解保留在 .class 文件中,并且在运行时可以通过反射机制读取。
  • @Documented注解就可加可不加了
  • 如果说咱哥们任性,我不加@Mapping注解,那你写这个GlobalInterceptor没什么用。

这里面,虽然定义了校验参数,但是参数里面还有相应的特性,我们还要定义一个注解。

代码语言:java
AI代码解释
复制
@Retention(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; // 正则表达式枚举
}

这样,注解就都定义好了,每个参数默认都是不校验的。

那么,注解定义完了,要怎么实现切面呢?

3.定义一个类

GlobalOperatcionAspect全局操作拦截

代码语言:java
AI代码解释
复制
@Aspect
@Component("globalOperatcionAspect")
public class GlobalOperatcionAspect {
}

在代码中,要用@Aspect注解来证明它是一个切面,用@Component注解交给spring管理

下面我就直接上完整代码了

代码语言:java
AI代码解释
复制
@Aspect
@Component("globalOperatcionAspect")
@Slf4j
public class GlobalOperatcionAspect {

    private static final String TYPE_STRING = "java.lang.String";
    private static final String TYPE_INTEGER = "java.lang.Integer";
    private static final String TYPE_LONG = "java.lang.Long";

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private AppConfig appConfig;
    @Pointcut("@annotation(这里换成你GlobalInterceptor的路径)") //定义切点
    private void requestInterceptor(){ //请求拦截切点

    }

    //事件通知before、after、around
    @Before("requestInterceptor()")
    public void interceptorDo(JoinPoint point) throws BusinessException {
        try {
            Object target = point.getTarget();
            Object[] arguments = point.getArgs();
            String methodName = point.getSignature().getName();
            Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
            Method method = target.getClass().getMethod(methodName, parameterTypes);
            GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);
            if (null == interceptor) {
                return;
            }
            /**
             * 校验登录
             */
            if (interceptor.checkLogin() || interceptor.checkAdmin()) {
                checkLogin(interceptor.checkAdmin());
            }
            /**
             * 校验参数
             */
            if (interceptor.checkParams()) {
                validateParams(method, arguments);
            }
        } catch (BusinessException e) {
            log.error("全局拦截器异常", e);
            throw e;
        } catch (Exception e) {
            log.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        } catch (Throwable e) {
            log.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        }
    }
    
    //校验登录
    private void checkLogin(Boolean checkAdmin) {
        //SpringBoot在一个类中拿到session信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //从session中获取登录用户信息
        SessionWebUserDto sessionUser = (SessionWebUserDto) session.getAttribute(Constants.SESSION_KEY);
        if (sessionUser == null && appConfig.getDev() != null && appConfig.getDev()) {
            List<UserInfo> userInfoList = userInfoService.findListByParam(new UserInfoQuery());
            if (!userInfoList.isEmpty()) {
                UserInfo userInfo = userInfoList.get(0);
                sessionUser = new SessionWebUserDto();
                sessionUser.setUserId(userInfo.getUserId());
                sessionUser.setNickName(userInfo.getNickName());
                sessionUser.setAdmin(true);
                session.setAttribute(Constants.SESSION_KEY, sessionUser);
            }
        }

        if (null == sessionUser) {
            throw new BusinessException(ResponseCodeEnum.CODE_901);
        }

        if (checkAdmin && !sessionUser.getAdmin()) {
            throw new BusinessException(ResponseCodeEnum.CODE_404);
        }
    }




    /**
     * 校验参数
     *
     * @param m
     * @param arguments
     * @throws BusinessException
     */
    private void validateParams(Method m, Object[] arguments) throws BusinessException {
        Parameter[] parameters = m.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object value = arguments[i];
            VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class);
            if (verifyParam == null) {
                continue;
            }
            //基本数据类型
            if (TYPE_STRING.equals(parameter.getParameterizedType().getTypeName()) || TYPE_LONG.equals(parameter.getParameterizedType().getTypeName()) || TYPE_INTEGER.equals(parameter.getParameterizedType().getTypeName())) {
                checkValue(value, verifyParam);
                //如果传递的是对象
            } else {
                checkObjValue(parameter, value);
            }
        }
    }

    private void checkObjValue(Parameter parameter, Object value) {
        try {
            String typeName = parameter.getParameterizedType().getTypeName();
            Class classz = Class.forName(typeName);
            Field[] fields = classz.getDeclaredFields();
            for (Field field : fields) {
                VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class);
                if (fieldVerifyParam == null) {
                    continue;
                }
                field.setAccessible(true);
                Object resultValue = field.get(value);
                checkValue(resultValue, fieldVerifyParam);
            }
        } catch (BusinessException e) {
            log.error("校验参数失败", e);
            throw e;
        } catch (Exception e) {
            log.error("校验参数失败", e);
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
    }

    /**
     * 校验参数
     *
     * @param value
     * @param verifyParam
     * @throws BusinessException
     */
    private void checkValue(Object value, VerifyParam verifyParam) throws BusinessException {
        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);
        }
    }


}

回到sendEmailCode方法里,我们就可以加入相应注解进行校验了

代码语言:java
AI代码解释
复制
    @GlobalInterceptor(checkParams = true, checkLogin = false)
    public ResponseVO sendEmailCode(HttpSession session,
                                    @VerifyParam(required = true, regex = VerifyRegexEnum.EMAIL, max = 150) String email,
                                    @VerifyParam(required = true) String checkCode,
                                    @VerifyParam(required = true) Integer type) {
        try {
            //解决空指针异常,需要判断
            //使用AOP做参数校验
            if (!checkCode.equalsIgnoreCase((String) session.getAttribute(Constants.CHECK_CODE_KEY_EMAIL))) {
                throw new BusinessException("图片验证码不正确");
            }
            emailCodeService.sendEmailCode(email, type);
            return getSuccessResponseVO(null);
        } finally {
            //每次用完这个验证码,不管成功或者失败,都要重置
            session.removeAttribute(Constants.CHECK_CODE_KEY_EMAIL);
        }

    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SpringBoot项目实战:自定义异常和统一参数验证(附源码)
在实际开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类BusinessException。
田维常
2023/08/31
7960
SpringBoot项目实战:自定义异常和统一参数验证(附源码)
Controller层代码这么写,简洁又优雅!
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/08/29
4000
Controller层代码这么写,简洁又优雅!
自定义注解判断参数为空
最近在项目中遇到了一个小小的问题,和大家分享一下,简单的接口但是在不同的业务场景下需要有不同的校验逻辑,有的参数在特定的场景下需要校验,有的参数在另外的场景下则不需要校验。解决方案有很多种加上我当时是刚刚入职为了偷懒贪图省事,所以就写了一大堆的if/else。如下展示(由于业务原因,敏感字段已转换):
@派大星
2023/06/28
3540
自定义注解判断参数为空
Pig4Cloud之验证码
this.loginForm.randomStr = randomLenNum(this.code.len, true);
一觉睡到小时候
2022/12/07
7440
开撸!SpringBoot-表单验证-统一异常处理-自定义验证信息源
我们都知道前台的验证只是为了满足界面的友好性、客户体验性等等。但是如果仅靠前端进行数据合法性校验,是远远不够的。因为非法用户可能会直接从客户端获取到请求地址进行非法请求,所以后台的校验是必须的;特别是应用如果不允许输入空值,对数据的合法性有要求的情况下。
IT大咖说
2021/09/08
2.6K0
开撸!SpringBoot-表单验证-统一异常处理-自定义验证信息源
Springboot实现小程序端发送邮箱验证码功能
打开QQ邮箱网页→设置→账户→POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务→开启POP3/SMTP服务,然后就能看到授权码了
零式的天空
2022/03/26
8460
Springboot实现小程序端发送邮箱验证码功能
022.自定义注解
注解是Jdk1.5新增新技术。很多框架为了简化代码,都会提供有些注解。 可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件。 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
qubianzhong
2018/12/14
4320
生成图形验证码是如此简单
依赖 <!-- kaptcha验证码--> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> 获取图形验证码 前端传入uuid,后端通过uuid作为redis缓存key import com.google.code.kaptcha.Constants; import com.google.c
用户8889406
2023/03/05
6750
统一异常处理介绍及实战
软件开发过程中,不可避免的是需要处理各种异常,就我自己来说,至少有一半以上的时间都是在处理各种异常情况,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。比较下面两张图,看看您现在编写的代码属于哪一种风格?然后哪种编码风格您更喜欢?
框架师
2019/09/19
1.4K0
统一异常处理介绍及实战
页面多查询条件必选的统一处理思路
开发中我们可能会遇到会页面对应的数据表量级较大、页面查询条件过多的情况,那么有时候我们可能会限制做查询操作是必须选择至少一个查询条件。
青衫染红尘
2022/11/09
1.1K0
页面多查询条件必选的统一处理思路
kaptcha验证码插件的使用
CBeann
2023/12/25
1890
kaptcha验证码插件的使用
SpringBoot系列之自定义枚举类的数据校验注解
SpringBoot系列之自定义枚举类的数据校验注解 业务场景:数据校验,需要对枚举类型的数据传参,进行数据校验,不能随便传参。拓展,支持多个参数的枚举数据校验
SmileNicky
2021/12/17
3.3K1
SpringBoot系列之自定义枚举类的数据校验注解
Mybatis-plus实现在DAO层面对敏感数据的加解密
日常我们在存储数据的时候,经常会碰到一些敏感性的数据,比如用户的身份证号和手机号等。这些数据一般是不允许我们在数据库中做明文存储的,这就需要我们在存储字段的时候,对这些字段做加密操作,同时在读取出来的时候,也要对相应字段做解密操作。
一缕82年的清风
2021/12/06
1.1K0
Spring学习笔记(9)一springMVC/boot全局异常处理和参数校验
我们使用springboot做 Restfull API,希望能全局处理异常,返回自定义错误码。类似:
黄规速
2022/04/14
9170
Spring学习笔记(9)一springMVC/boot全局异常处理和参数校验
第2章 预约管理-检查项管理
云和健康管理系统是一款应用于健康管理机构的业务系统,实现健康管理机构工作内容可视化、患者管理专业化、健康评估数字化、健康干预流程化、知识库集成化,从而提高健康管理师的工作效率,加强与患者间的互动,增强管理者对健康管理机构运营情况的了解。
张哥编程
2024/12/13
1700
Java注解Annotation与自定义注解详解
开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解。
yaphetsfang
2020/07/30
9130
手把手0基础项目实战(三)——教你开发一套电商平台的安全框架
写在最前 本文是《手把手项目实战系列》的第三篇文章,预告一下,整个系列会介绍如下内容: 《手把手0基础项目实战(一)——教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)》 《手把手0基础项目实战(二)——微服务架构下的数据库分库分表实战》 《手把手0基础项目实战(三)——教你开发一套安全框架》 《手把手0基础项目实战(四)——电商订单系统架构设计与实战(分布式事务一致性保证)》 《手把手0基础项目实战(五)——电商系统的缓存策略》 《手把手0基础项目实战
大闲人柴毛毛
2018/03/29
1.4K0
手把手0基础项目实战(三)——教你开发一套电商平台的安全框架
Spring Boot 3 整合Hutool-captcha实现图形验证码
在整合技术框架的时候,想找一个图形验证码相关的框架,看到很多验证码不在更新了或者是在中央仓库下载不下来,还需要多引入依赖。后面看到了Hutool 图形验证码(Hutool-captcha)中对验证码的实现,提供了:线段干扰验证码、圆圈干扰验证码、扭曲干扰验证码以及自定义验证码。就此验证码在项目中的使用展开说明。
Harry技术
2025/01/12
5590
Spring Boot 3 整合Hutool-captcha实现图形验证码
SpringBoot 定义优雅全局统一 Restful API 响应和统一异常处理,太优雅了!
假如你作为项目组长,为 Spring Boot 项目设计一个规范的统一的RESTfulAPI 响应框架。
码哥字节
2024/11/23
5180
SpringBoot 定义优雅全局统一 Restful API 响应和统一异常处理,太优雅了!
SpringBoot实现通用的接口参数校验,注解实战
目前参数校验常用的方法是在实体类上添加注解,但对于不同的方法,所应用的校验规则也是不一样的,例如有一个 AccountVO实体:
用户4283147
2022/10/08
5200
推荐阅读
相关推荐
SpringBoot项目实战:自定义异常和统一参数验证(附源码)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档