• 使用aop结合redis进行方法参数的签名等验证


    在controller中直接对方法进行验证,最初想着简单省事;因为springboot貌似对数据请求二次解析,需要请求转发处理,后续补上;

    1.自定义注解
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SignValidate {
        String name() default "";
    }

    2.解析注解,使用Before(),抛出异常 全局异常处理的方式;
    若使用aroud(),依然会走方法体;
    /***
     * 验证sign
     * appid  appsecret Header
     * timestamp 13位毫秒 Header
     * sign MD5(appid+appkey+timestamp) 32位小写 Header 
     *   1.appkey最好存在服务端
     *   2.RSA更安全
     */
    @Slf4j
    @Aspect
    @Component
    public class SignValidateAspect {
        private final static Long REDIS_SIGN_EXPIRE_TIME = 5 * 60 * 1000L;
        private final static String REDIS_KEY_PREFIX = "APPID:";
        private final static String REDIS_APPSIGN_PREFIX = "APPSIGN:";

        @Autowired
        StringRedisTemplate redisTemplate;

        @Autowired
        SysApplicationService sysApplicationService;

        @Pointcut("@annotation(com.xx.xx.annotation.SignValidate)")
        public void pointCut() {
        }

        // 验证时间是否有效;验证sign是否有效;验证sign是否被使用过,防止被人截取一直请求
        @Before(value = "pointCut()")
        public void before(JoinPoint point) {
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = sra.getRequest();
            String timestamp = request.getHeader("timestamp");
            String appid = request.getHeader("appid");
            String sign = request.getHeader("sign");

            if (StringUtils.isBlank(appid) || StringUtils.isBlank(sign) || StringUtils.isBlank(timestamp)) {
                throw new SignValidateException(ReturnCodeEnum.INVALID_HEADER);
            }

            // 验证时间是否有效
            long now = System.currentTimeMillis();
            if (Math.abs(now - Long.valueOf(timestamp)) >= REDIS_SIGN_EXPIRE_TIME) {
                // 过期
                throw new SignValidateException(ReturnCodeEnum.INVALID_TIMESTAMP);
            }

            // 验证sign MD5(appid+appkey+timestamp) 也可以将appkey放在配置文件中
    //        String appSecret = redisTemplate.opsForValue().get(REDIS_KEY_PREFIX + appid);
            String appSecret = sysApplicationService.getAppSecret(appid);
            if (StringUtils.isBlank(appSecret)) {
                appSecret = DigestUtils.md5DigestAsHex(appid.getBytes(StandardCharsets.UTF_8)).substring(10, 14);
            }
            String md5Str = appid + appSecret + timestamp;
            log.info(md5Str);
            String encode = DigestUtils.md5DigestAsHex(md5Str.getBytes(StandardCharsets.UTF_8));
            log.info(encode);

            if (!StringUtils.equals(sign, encode) || redisTemplate.hasKey(REDIS_APPSIGN_PREFIX + sign)) {
                throw new SignValidateException(ReturnCodeEnum.INVALID_SIGN);
            }

            // 保存sign
            redisTemplate.opsForValue()
                    .set(REDIS_APPSIGN_PREFIX + sign, "1", REDIS_SIGN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        }

    }


    3.自定义异常
    @Data
    public class SignValidateException extends RuntimeException {
        private ReturnCodeEnum returnCodeEnum;

        private SignValidateException() {}

        public SignValidateException(ReturnCodeEnum returnCodeEnum) {
            super(returnCodeEnum.getMsg());
            this.returnCodeEnum = returnCodeEnum;
        }
    }

    4.全局异常处理@RestcontrollerAdvice
    @RestControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {

        /**
         * @param e
         * @return
         * @Validated 验证签名
         */
        @ResponseStatus(HttpStatus.OK)
        @ExceptionHandler(SignValidateException.class)
        public ResponseWrapper handler(SignValidateException e) throws IOException {
            return ResponseWrapper.error(e.getReturnCode());
        }

        /**
         * 系统异常
         *
         * @param e
         * @return
         */
        @ResponseStatus(HttpStatus.OK)
        @ExceptionHandler(value = Exception.class)
        public ResponseWrapper exceptionHandler(Exception e) {
            log.info(e.getMessage(),e);
            return ResponseWrapper.error(e.getMessage());
        }
    }

    5.在需要验证的地方加@SignValidate

  • 相关阅读:
    三七总皂苷脂质体纳米粒子修饰负载RNA核糖核酸(实验注意事项)
    干货 | 背熟这些 Docker 命令,面试再也不怕啦~
    c#using关键字的作用
    【学习记录】tensorflow图像数据增强
    Python + Django4 搭建个人博客(六): 数据库表和模型设计
    shell脚本编程基础(中)
    [附源码]计算机毕业设计小区疫情事件处理系统Springboot程序
    MySQL 高级语句 Part1(进阶查询语句+MySQL数据库函数+连接查询)
    Java后端 - 一面凉经 - 得物(国际电商)
    外包干了3个月,技术退步明显。。。。。
  • 原文地址:https://blog.csdn.net/bighacker/article/details/127965452