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

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

原创
作者头像
xiao李
发布2024-07-27 22:11:39
960
发布2024-07-27 22:11:39
举报
文章被收录于专栏:我吃香菜青椒不挑食

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

1.导包

代码语言:xml
复制
    <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
复制
    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
复制
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
复制
@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
复制
@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
复制
@Aspect
@Component("globalOperatcionAspect")
public class GlobalOperatcionAspect {
}

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

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

代码语言:java
复制
@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
复制
    @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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.导包
  • 2.定义注解
  • 3.定义一个类
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档