• 使用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

  • 相关阅读:
    React(2)
    常用与业务密切相关的prompt
    解析java数值类型数据混合运算
    训练正常&异常的GAN损失函数loss变化应该是怎么样的
    数据库和缓存如何保证一致性?
    「Flask」路由+视图函数
    「SpringCloud」08 Config分布式配置中心
    关于#java#的问题,请各位专家解答!
    迅为龙芯3A5000主板,支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 VGA,可直连显示器
    Go对Json的支持[encoding/json]
  • 原文地址:https://blog.csdn.net/bighacker/article/details/127965452