• java使用拦截器进行接口签名验证


    之前有使用aop自定义注解的方式;

    本篇签名验证都一样;时效;防抓包重复请求;redis验签

    拦截器方法感觉解耦更强吧,结合全局异常(之前文章里有-aop自定义注解

    1.签名验证拦截器
    @Component
    @Slf4j
    public class SignAuthInterceptor implements HandlerInterceptor {
        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:FILESERVER:";

        @Autowired
        StringRedisTemplate redisTemplate;

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String timestamp = request.getHeader("timestamp");
            String appid = request.getHeader("appid");
            String sign = request.getHeader("sign");
            String method = request.getMethod();

            if(StringUtils.equals("POST", method)) {
                // 使用getInputStream后,controller方法参数获取不到 
                // 简单的思路就是将需要签名验证的参数放到header,暂时只用到header
                // 若签名对参数加密,可以添加过滤器处理requestBody, 即二次转发数据
    //            InputStream inputStream = request.getInputStream();
    //            String param = FileUtil.readJsonFile(inputStream);
    //            log.info(param);
            }

            if (StringUtils.isBlank(appid) || StringUtils.isBlank(sign) || StringUtils.isBlank(timestamp)) {
                returnJson(response, ReturnCode.INVALID_HEADER);
                return false;
            }

            // 验证时间是否有效
            long now = System.currentTimeMillis();
            if (Math.abs(now - Long.valueOf(timestamp)) >= REDIS_SIGN_EXPIRE_TIME) {
               returnJson(response, ReturnCode.INVALID_TIMESTAMP);
               return false;
            }

            // 验证sign MD5(appid+appkey+timestamp) 此处,省事 默认secret为Md5(appId).substring(10,14)
            String appSecret = "";
            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)) {
                returnJson(response, ReturnCode.INVALID_SIGN);
                return false;
            }

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

            return HandlerInterceptor.super.preHandle(request, response, handler);
        }

        // 异常返回值;可以用全局异常替代,之前的文章里有
        // ReturnCode 为枚举返回值
        private void returnJson(HttpServletResponse response, ReturnCode returnCode){
            PrintWriter writer = null;
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            try {
                writer = response.getWriter();
                writer.print(JSON.toJSONString(ResponseWrapper.error(returnCode)));
            } catch (IOException e){
                log.error(e.getMessage());
            } finally {
                if(writer != null){
                    writer.close();
                }
            }
        }
    }


    2.添加全局配置
    @Configuration
    public class SignAuthWebConfig implements WebMvcConfigurer {

        @Autowired
        private SignAuthInterceptor signAuthInterceptor;

        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 添加拦截规则
            registry.addInterceptor(signAuthInterceptor).addPathPatterns("/test/**");
        }

          // 若对参数验签,此处添加过滤器配置;防止接口层不能获取RequestBody值
    //    @Bean
    //    public FilterRegistrationBean registrationBean() {
    //        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
    //        filterRegistrationBean.addUrlPatterns("/*");
    //
    //        return filterRegistrationBean;
    //    }

    }

  • 相关阅读:
    ceph部署
    别再纠结线程池池大小、线程数量了,哪有什么固定公式
    基于Android的个人健康管理系统
    惹恼开源社区!微软道歉:恢复 .NET SDK 热重载功能
    西门子ACSON X300E如何通过网线与计算机连接
    Nginx网络服务之监控模块
    【Python爬虫 js渲染思路一】
    Django 基础教程
    【Java EE初阶二十六】简单的表白墙(二)
    单片机的冷启动、热启动、复位
  • 原文地址:https://blog.csdn.net/bighacker/article/details/128017165