java防重提交

摘要:
通过注解限制方法规定时间内只能请求一次@RepeatSubmit(time=10)搭配方法后面加上动态路径效果更佳@RequestMapping("/install/{id}")@ResponseBodyRepeatedlyRequestWrapperpackagecom.platform.client.interceptor;importcom.get.common.core.utils.htt
通过注解限制 方法规定时间内只能请求一次
@RepeatSubmit(time = 10)
 搭配方法 后面加上动态路径 效果更佳 
@RequestMapping("/install/{id}")
@ResponseBody
java防重提交第1张

java防重提交第2张

java防重提交第3张

RepeatedlyRequestWrapper
package com.platform.client.interceptor;

import com.get.common.core.utils.http.HttpHelper;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 构建可重复读取inputStream的request
 *
 */
public classRepeatedlyRequestWrapper extends HttpServletRequestWrapper
{
    private final byte[] body;

    publicRepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
    {
        super(request);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        body = HttpHelper.getBodyString(request).getBytes("UTF-8");
    }

    @Override
    publicBufferedReader getReader() throws IOException
    {
        return new BufferedReader(newInputStreamReader(getInputStream()));
    }

    @Override
    publicServletInputStream getInputStream() throws IOException
    {
        final ByteArrayInputStream bais = newByteArrayInputStream(body);
        return newServletInputStream()
        {
            @Override
            public intread() throws IOException
            {
                returnbais.read();
            }

            @Override
            public intavailable() throws IOException
            {
                returnbody.length;
            }

            @Override
            publicboolean isFinished()
            {
                return false;
            }

            @Override
            publicboolean isReady()
            {
                return false;
            }

            @Override
            public voidsetReadListener(ReadListener readListener)
            {

            }
        };
    }
}
RepeatSubmitInterceptor
package com.platform.client.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.get.common.core.annotation.RepeatSubmit;
import com.get.common.core.utils.ServletUtils;
import com.get.common.core.web.domain.AjaxResult;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 防止重复提交拦截器
 *
 */@Component
public abstract classRepeatSubmitInterceptor extends HandlerInterceptorAdapter
{
    @Override
    publicboolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if(handler instanceof HandlerMethod)
        {
            HandlerMethod handlerMethod =(HandlerMethod) handler;
            Method method =handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null)
            {
                if (this.isRepeatSubmit(request))
                {
                    AjaxResult ajaxResult = AjaxResult.error("不用着急,稍后再试");
                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
                    return false;
                }
            }
            return true;
        }
        else{
            returnsuper.preHandle(request, response, handler);
        }
    }

    /**
     * 验证是否重复提交由子类实现具体的防重复提交的规则
     *
     * @param request
     * @return
     * @throws Exception
     */
    public abstractboolean isRepeatSubmit(HttpServletRequest request);
}
SameUrlDataInterceptor
package com.platform.client.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.get.common.core.constant.CacheConstants;
import com.get.common.core.constant.Constants;
import com.get.common.core.utils.StringUtils;
import com.get.common.core.utils.http.HttpHelper;
import com.get.common.redis.service.Redis1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 判断请求url和数据是否和上一次相同,
 * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
 *
 */@Component
public classSameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";

    public final String REPEAT_TIME = "repeatTime";

    //令牌自定义标识
//@Value("${token.header}")
    private String header=CacheConstants.HEADER;

    @Autowired
    privateRedis1Service redisCache;

    /**
     * 间隔时间,单位:秒 默认10秒
     *
     * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
     */
    private int intervalTime = 10;

    public void setIntervalTime(intintervalTime)
    {
        System.out.println("2222222222222222222222222222222222222222222222222222222222222222222222222222");
        this.intervalTime =intervalTime;
    }

    @SuppressWarnings("unchecked")
    @Override
    publicboolean isRepeatSubmit(HttpServletRequest request)
    {
        String nowParams = "";
        if(request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest =(RepeatedlyRequestWrapper) request;
            nowParams =HttpHelper.getBodyString(repeatedlyRequest);
        }

        //body参数为空,获取Parameter的数据
        if(StringUtils.isEmpty(nowParams))
        {
            nowParams =JSONObject.toJSONString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());

        //请求地址(作为存放cache的key值)
        String url =request.getRequestURI();

        //唯一值(没有消息头则使用请求地址)
        String submitKey =request.getHeader(header);
        if(StringUtils.isEmpty(submitKey))
        {
            submitKey =url;
        }

        //唯一标识(指定key + 消息头)
        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY +submitKey;

        Object sessionObj =redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null)
        {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            if(sessionMap.containsKey(url))
            {
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) &&compareTime(nowDataMap, preDataMap))
                {
                    return true;
                }
            }
        }
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap,(long)intervalTime, TimeUnit.SECONDS);
        return false;
    }

    /**
     * 判断参数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object>preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        returnnowParams.equals(preParams);
    }

    /**
     * 判断两次间隔时间
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object>preMap)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < (this.intervalTime * 1000))
        {
            return true;
        }
        return false;
    }
}
ResourcesConfig
package com.platform.client.config;

import com.platform.client.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 通用配置
 *
 */@Configuration
public classResourcesConfig implements WebMvcConfigurer
{
    @Autowired
    privateRepeatSubmitInterceptor repeatSubmitInterceptor;


    /**
     */@Override
    public voidaddInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
    }
}
RepeatSubmit
package com.get.common.core.annotation;

import java.lang.annotation.*;

/**
 * 自定义注解防止表单重复提交
 * 
 * @author ruoyi
 *
 */@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interface RepeatSubmit
{

    int time() default 10;
}
jar包
 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

免责声明:文章转载自《java防重提交》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux命令:chownweb安全测试之二下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle加密解密

Oracle内部有专门的加密包,可以很方便的对内部数据进行加密(encrypt)和解密(decrypt).   介绍加密包之前,先简单说一下Oracle基本数据类型——RAW类型。   RAW,用于保存位串的数据类型,类似于CHAR,声明方式RAW(L),L为长度,以字节为单位,作为数据库列最大2000,作为变量最大32767字节。      操作RAW类...

在C#中利用SharpZipLib进行文件的压缩和解压缩收藏

我在做项目的时候需要将文件进行压缩和解压缩,于是就从http://www.icsharpcode.net(http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx)下载了关于压缩和解压缩的源码,但是下载下来后,面对这么多的代码,一时不知如何下手。只好耐下心来,慢慢的研究,总算找到了门路。...

.net 分布式架构之分布式锁实现

分布式锁 经常用于在解决分布式环境下的业务一致性和协调分布式环境。 实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等。 使用分布式锁的场景一般不能太多。 开源地址:http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedLock 开源相关群: .net 开源基础服...

024. asp.net中第一次使用GridView (设置鼠标经过时更换背景色)

1. 前端HTML代码 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Index.aspx.cs" Inherits="Index" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht...

EFCore梳理

一、CodeFirst模式 官网:https://docs.microsoft.com/zh-cn/ef/core/get-started/overview/first-app?tabs=visual-studio 下面进行一个简单的示例。 代码:https://github.com/qiuxianhu/EFCoreSummary 1、创建一个.NETCo...

CodeIgniter 的数据安全过滤全解析

http://justcoding.iteye.com/blog/546880 CodeIgniter 的数据安全过滤全解析博客分类: Codeigniter / CakePHP MySQLSQLPHP 由于对CI的SQL安全这些不放心,今天寡人啃了一下午的代码,算是对其机制比较了解了,为了让各位兄弟姐妹少走弯路,特将战果公布,希望大家喜欢。 1.无...