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

2024-07-27 22:11:39 浏览数 (3)

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);
        }

    }

0 人点赞