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

本文介绍了如何在Spring MVC应用中使用拦截器进行签名验证,以增强安全性。通过实现HandlerInterceptor接口,创建了一个SignAuthInterceptor,该拦截器负责检查请求头中的timestamp、appid和sign,确保请求的有效性和防止重复请求。同时,利用Redis存储签名校验状态,实现了时效性和防抓包功能。此外,配置了全局拦截器,将签名验证逻辑整合,提高了代码的解耦性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前有使用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;
//    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值