优雅的接口防刷处理方案! 下

简介: 优雅的接口防刷处理方案! 下

5、创建过滤器实现安全校验

@Configuration
public class SignFilterConfiguration {
    @Value("${sign.maxTime}")
    private String signMaxTime;
    //filter中的初始化参数
    private Map<String, String> initParametersMap =  new HashMap<>();
    @Bean
    public FilterRegistrationBean contextFilterRegistrationBean() {
        initParametersMap.put("signMaxTime",signMaxTime);
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(signFilter());
        registration.setInitParameters(initParametersMap);
        registration.addUrlPatterns("/sign/*");
        registration.setName("SignFilter");
        // 设置过滤器被调用的顺序
        registration.setOrder(1);
        return registration;
    }
    @Bean
    public Filter signFilter() {
        return new SignFilter();
    }
}
@Slf4j
public class SignFilter implements Filter {
    @Resource
    private RedisUtil redisUtil;
    //从fitler配置中获取sign过期时间
    private Long signMaxTime;
    private static final String NONCE_KEY = "x-nonce-";
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        log.info("过滤URL:{}", httpRequest.getRequestURI());
        HttpServletRequestWrapper requestWrapper = new SignRequestWrapper(httpRequest);
        //构建请求头
        RequestHeader requestHeader = RequestHeader.builder()
                .nonce(httpRequest.getHeader("x-Nonce"))
                .timestamp(Long.parseLong(httpRequest.getHeader("X-Time")))
                .sign(httpRequest.getHeader("X-Sign"))
                .build();
        //验证请求头是否存在
        if(StringUtils.isEmpty(requestHeader.getSign()) || ObjectUtils.isEmpty(requestHeader.getTimestamp()) || StringUtils.isEmpty(requestHeader.getNonce())){
            responseFail(httpResponse, ReturnCode.ILLEGAL_HEADER);
            return;
        }
        /*
         * 1.重放验证
         * 判断timestamp时间戳与当前时间是否操过60s(过期时间根据业务情况设置),如果超过了就提示签名过期。
         */
        long now = System.currentTimeMillis() / 1000;
        if (now - requestHeader.getTimestamp() > signMaxTime) {
            responseFail(httpResponse,ReturnCode.REPLAY_ERROR);
            return;
        }
        //2. 判断nonce
        boolean nonceExists = redisUtil.hasKey(NONCE_KEY + requestHeader.getNonce());
        if(nonceExists){
            //请求重复
            responseFail(httpResponse,ReturnCode.REPLAY_ERROR);
            return;
        }else {
            redisUtil.set(NONCE_KEY+requestHeader.getNonce(), requestHeader.getNonce(), signMaxTime);
        }
        boolean accept;
        SortedMap<String, String> paramMap;
        switch (httpRequest.getMethod()){
            case "GET":
                paramMap = HttpDataUtil.getUrlParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            case "POST":
                paramMap = HttpDataUtil.getBodyParams(requestWrapper);
                accept = SignUtil.verifySign(paramMap, requestHeader);
                break;
            default:
                accept = true;
                break;
        }
        if (accept) {
            filterChain.doFilter(requestWrapper, servletResponse);
        } else {
            responseFail(httpResponse,ReturnCode.ARGUMENT_ERROR);
            return;
        }
    }
    private void responseFail(HttpServletResponse httpResponse, ReturnCode returnCode)  {
        ResultData<Object> resultData = ResultData.fail(returnCode.getCode(), returnCode.getMessage());
        WebUtils.writeJson(httpResponse,resultData);
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String signTime = filterConfig.getInitParameter("signMaxTime");
        signMaxTime = Long.parseLong(signTime);
    }
}

6、Redis工具类

@Component
public class RedisUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

项目源码地址

https://github.com/jianzh5/cloud-blog/tree/main/cloud-demo



相关文章
|
关系型数据库 MySQL
MySQL主从异常Coordinator stopped because there were error(s) in the worker(s). The most recent failur
MySQL主从异常Coordinator stopped because there were error(s) in the worker(s). The most recent failur
5691 0
|
分布式计算 DataWorks 监控
dataworks组件
DataWorks 可能会添加新的功能或改进现有的组件,因此建议查阅最新的官方文档以获取最准确的信息。
421 2
|
存储 小程序 容器
如何制作一个微信答题小程序?
如何制作一个微信答题小程序?
841 0
|
存储 Ubuntu 机器人
机械臂手眼标定详解
这篇文章是关于机械臂手眼标定的详细教程,包括了使用ROS1 Noetic和Realsense D415相机在Ubuntu 20.04环境下进行标定的步骤和配置方法。
3118 0
机械臂手眼标定详解
|
消息中间件 Kafka 数据安全/隐私保护
RabbitMQ异步通信详解
RabbitMQ异步通信详解
651 17
|
前端开发 应用服务中间件 nginx
[译] 面向 React 和 Nginx 的 Docker 多阶段构建
[译] 面向 React 和 Nginx 的 Docker 多阶段构建
[译] 面向 React 和 Nginx 的 Docker 多阶段构建
|
数据采集 监控 关系型数据库
CDC 与 Oceanbase 的激情碰撞:实时采集数据的震撼之旅,颠覆数据世界的神秘冒险!
【8月更文挑战第7天】在数据处理领域,实时采集变得至关重要。OceanBase是一款高性能、可扩展的分布式数据库。通过变更数据捕获(CDC)技术实时采集其数据是一项关键技术。利用如Debezium等工具,可以实现OceanBase的数据变动捕捉。示例代码展示了如何配置Debezium以监听OceanBase的数据变更。实际应用中需按业务需求定制数据处理逻辑,并实施监控与错误管理以保障采集的准确性和稳定性,从而为业务提供实时数据支持,推动创新发展。
468 1
|
敏捷开发 小程序 持续交付
阿里云云效产品使用合集之代码库的容量限制是多少
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
负载均衡 监控 网络协议
TCP四次挥手:为什么四次?原理大揭密!
**TCP四次挥手详解**:客户端发送FIN进入FIN-WAIT-1,服务器回ACK进CLOSE-WAIT;服务器发送FIN,客户端回ACK进TIME-WAIT,等待2MSL确保数据传输完毕,防止新旧连接混淆。四次挥手确保双方完全关闭连接,解决数据丢失问题。过多TIME-WAIT可通过负载均衡、优化关闭顺序或调整系统参数缓解。关注“软件求生”获取更多技术内容!
424 0
|
存储 Java 定位技术
SpringBoot轻松实现二维码条形码含源码案例
SpringBoot轻松实现二维码条形码含源码案例
507 1

热门文章

最新文章