拦截器魔法:Spring MVC中的防重放守护者

目录

  1. 简介
  2. [HandlerInterceptorAdapter vs HandlerInterceptor](#HandlerInterceptorAdapter vs HandlerInterceptor)
  3. 创建一个防重放拦截器
  4. 注册拦截器
  5. 路径模式匹配
  6. 适配器模式的魅力
  7. 总结

简介

在构建安全可靠的Web应用程序时,防止请求重放攻击是一项关键任务。当用户或系统发出的请求被恶意第三方捕获并重复发送给服务器时,就会发生重放攻击。这可能导致不必要的操作被执行,如重复下单、重复支付等。为了保护我们的应用免受此类威胁,我们可以利用Spring MVC框架提供的强大工具------HandlerInterceptor来实现防重放机制。

HandlerInterceptorAdapter vs HandlerInterceptor

当你决定为自己的应用添加自定义拦截逻辑时,可能会面临选择:是继承 HandlerInterceptorAdapter 还是直接实现 HandlerInterceptor 接口?两者之间的差异在于设计哲学和代码的简洁性。

  • HandlerInterceptorAdapter 是一个适配器类,它实现了 HandlerInterceptor 接口,并提供了所有方法的空实现。因此,如果你只需要覆盖部分方法,使用这个适配器可以让你的代码更加简洁。
  • HandlerInterceptor 接口则要求你必须显式地实现接口中定义的所有方法。这种方式虽然可能引入一些冗余代码,但它提供了更高的透明度,因为你清楚地知道有哪些方法需要处理。
  • 总结:extends HandlerInterceptorAdapter 只重写需要的方法即可,implements HandlerInterceptor需要重写所有方法,可能增加代码冗余

创建一个防重放拦截器

为了确保相同的请求不会被多次处理,我们需要在每次接收到POST请求时检查其唯一标识符(例如订单号或时间戳),并与服务器端存储的历史记录进行比较。下面是一个简单的防重放拦截器实现示例,这里我们使用Redis作为存储介质:

java 复制代码
@Component
public class AntiReplayInterceptor extends HandlerInterceptorAdapter {

    private static final String REQUEST_ID = "requestId";
    private static final long EXPIRE_TIME_IN_SECONDS = 60 * 5;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("POST".equals(request.getMethod()) && 
            request.getRequestURI().startsWith("/inner/public/syncOrder/v1")) {
            
            // 获取请求体中的唯一标识符
            SynYxcRequest orderSyncDto = extractRequestBody(request);
            String requestId = orderSyncDto.getRequestId();
            
            // 检查是否已经处理过该请求ID
            if (redisTemplate.opsForValue().get(requestId) != null) {
                response.setStatus(HttpServletResponse.SC_CONFLICT);
                return false;
            } else {
                // 将请求ID存入Redis,并设置过期时间
                redisTemplate.opsForValue().set(requestId, "processed", EXPIRE_TIME_IN_SECONDS, TimeUnit.SECONDS);
            }
        }

        return true;
    }

    private SynYxcRequest extractRequestBody(HttpServletRequest request) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(request.getReader().lines().collect(Collectors.joining(System.lineSeparator())), SynYxcRequest.class);
    }
}

注册拦截器

有了拦截器之后,我们需要将其注册到Spring MVC配置中,以确保它能够应用于指定的请求路径:

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AntiReplayInterceptor antiReplayInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(antiReplayInterceptor)
                .addPathPatterns("/inner/public/syncOrder/v1");
    }
}

路径模式匹配

如果不指定 .addPathPatterns() 方法,那么拦截器将会默认应用到所有路径。为了避免不必要的性能开销以及潜在的问题,你应该总是明确地定义你要拦截的路径模式。此外,你还可以使用 .excludePathPatterns() 来排除不需要拦截的路径。

适配器模式的魅力

通过使用 HandlerInterceptorAdapter,你可以轻松地只覆盖那些对你重要的方法,而无需关心其他方法。这种设计不仅简化了代码,也提高了可维护性。当然,如果你更喜欢一切都在掌控之中,直接实现 HandlerInterceptor 接口也是一个不错的选择。

总结

通过本文介绍的内容,我们了解了如何利用Spring MVC中的 HandlerInterceptorHandlerInterceptorAdapter 来构建一个高效的防重放拦截器。无论你是追求代码简洁还是完全控制,Spring都提供了灵活的解决方案,帮助你在Web开发的世界里游刃有余。希望这些知识能为你的项目增添一份安全保障,让每一次请求都能得到恰当的处理。

相关推荐
岁忧几秒前
(nice!!!)(LeetCode 每日一题) 3363. 最多可收集的水果数目 (深度优先搜索dfs)
java·c++·算法·leetcode·go·深度优先
陌上 烟雨齐3 小时前
Kafka数据生产和发送
java·分布式·kafka
Jinkxs3 小时前
高级15-Java构建工具:Maven vs Gradle深度对比
java·开发语言·maven
有梦想的攻城狮3 小时前
spring中的ApplicationRunner接口详解
java·后端·spring·runner·application
程序视点3 小时前
设计模式之原型模式!附Java代码示例!
java·后端·设计模式
振鹏Dong4 小时前
微服务架构及常见微服务技术栈
java·后端
丶小鱼丶4 小时前
二叉树算法之【中序遍历】
java·算法
摇滚侠5 小时前
Oracle 关闭 impdp任务
java
编程爱好者熊浪6 小时前
RedisBloom使用
java
苇柠6 小时前
Spring框架基础(1)
java·后端·spring