日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

使用自定義注解實現防止表單重復提交

我們還是先引入 Maven 依賴

<!-- Spring框架基本的核心工具 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>

<!-- SpringWeb模塊 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
</dependency>
<!-- 自定義驗證注解 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具類 -->
<dependency>
    <groupId>org.Apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>
<!-- io常用工具類 -->
<dependency>
     <groupId>commons-io</groupId>
     <artifactId>commons-io</artifactId>
 </dependency>
<!-- JSON工具類 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
    <groupId>JAVAx.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
</dependency>
復制代碼

接下來我們實現一個注解類(注解在 Java 中與類、接口的聲明類似,只是所使用的關鍵字有所不同,聲明注解使用 @interface 關鍵字。在底層實現上,所有定義的注解都會自動繼承
java.lang.annotation.Annotation 接口。)

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定義注解防止表單重復提交
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
    /**
     * 間隔時間(ms),小于此時間視為重復提交
     */
    public int interval() default 5000;

    /**
     * 提示消息
     */
    public String message() default "不允許重復提交,請稍后再試";
}
復制代碼

P.S. 關于自定義注解的解釋在之前的博客中有寫到,這里就不再多說了,有需要了解的小伙伴請移步至:大聰明教你學Java | Spring Boot 使用自定義注解實現操作日志的記錄

下面我們再實現一個攔截器,對提交表單的過程做一個相應的攔截校驗

import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 防止重復提交攔截器
 */
@Component
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
{
    @Override
    public boolean 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, annotation))
                {
                    //如果本次提交被認為是重復提交,則在此處做具體的邏輯處理
                    //如:彈出警告窗口等
                    return false;
                }
            }
            return true;
        }
        else
        {
            return true;
        }
    }

    /**
     * 驗證是否重復提交由子類實現具體的防重復提交的規則
     *
     * @param request 請求對象
     * @param annotation 防復注解
     * @return 結果
     */
    public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception;
}
復制代碼

我們可以看到再攔截器中一共有兩個方法,分別是 preHandle 和 isRepeatSubmit。無論我們執行什么請求,都會進入 RepeatSubmitInterceptor 攔截器,進入攔截器后先執行 preHandle 方法進行預處理,判斷本次攔截的方法是否增加了 RepeatSubmit 自定義注解,如果增加了該注解才會進行具體的校驗。isRepeatSubmit 方法是防止表單重復提交的規則,我們通過子類來實現 isRepeatSubmit 具體的校驗規則

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.mumu.framework.interceptor.RepeatSubmitInterceptor;
import org.springframework.stereotype.Component;
import com.mumu.common.annotation.RepeatSubmit;
import com.xxx.xxx.xxx.JSON; 

/**
 * 判斷請求url和數據是否和上一次相同
 * com.xxx.xxx.xxx.JSON; 是自定義的json工具類,具體代碼貼在后面
 */
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";

    public final String REPEAT_TIME = "repeatTime";

    public final String SESSION_REPEAT_KEY = "repeatData";

    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception
    {
        // 本次參數及系統時間
        String nowParams = JSON.marshal(request.getParameterMap());
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());

        // 請求地址(作為存放session的key值)
        String url = request.getRequestURI();

        HttpSession session = request.getSession();
        Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);
        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, annotation.interval()))
                {
                    return true;
                }
            }
        }
        Map<String, Object> sessionMap = new HashMap<String, Object>();
        sessionMap.put(url, nowDataMap);
        session.setAttribute(SESSION_REPEAT_KEY, sessionMap);
        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);
        return nowParams.equals(preParams);
    }

    /**
     * 判斷兩次間隔時間
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < interval)
        {
            return true;
        }
        return false;
    }
}
復制代碼

自定義 Json 工具類

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMAppingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

/**
 * JSON解析處理
 */
public class JSON
{
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();

    public static void marshal(File file, Object value) throws Exception
    {
        try
        {
            objectWriter.writeValue(file, value);
        }
        catch (JsonGenerationException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static void marshal(OutputStream os, Object value) throws Exception
    {
        try
        {
            objectWriter.writeValue(os, value);
        }
        catch (JsonGenerationException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static String marshal(Object value) throws Exception
    {
        try
        {
            return objectWriter.writeValueAsString(value);
        }
        catch (JsonGenerationException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static byte[] marshalBytes(Object value) throws Exception
    {
        try
        {
            return objectWriter.writeValueAsBytes(value);
        }
        catch (JsonGenerationException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static <T> T unmarshal(File file, Class<T> valueType) throws Exception
    {
        try
        {
            return objectMapper.readValue(file, valueType);
        }
        catch (JsonParseException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static <T> T unmarshal(InputStream is, Class<T> valueType) throws Exception
    {
        try
        {
            return objectMapper.readValue(is, valueType);
        }
        catch (JsonParseException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static <T> T unmarshal(String str, Class<T> valueType) throws Exception
    {
        try
        {
            return objectMapper.readValue(str, valueType);
        }
        catch (JsonParseException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }

    public static <T> T unmarshal(byte[] bytes, Class<T> valueType) throws Exception
    {
        try
        {
            if (bytes == null)
            {
                bytes = new byte[0];
            }
            return objectMapper.readValue(bytes, 0, bytes.length, valueType);
        }
        catch (JsonParseException e)
        {
            throw new Exception(e);
        }
        catch (JsonMappingException e)
        {
            throw new Exception(e);
        }
        catch (IOException e)
        {
            throw new Exception(e);
        }
    }
}

復制代碼

在校驗的過程中主要針對三個部分做校驗:請求地址、請求參數以及請求時間,只有當請求地址和請求參數一致且兩次請求的時間間隔小于xx毫秒時(xx毫秒即為在自定義注解中指定的時間),才會被判定為是重復提交。這種校驗方式較為繁瑣(畢竟需要比對三部分內容),但是它可以盡可能的避免出現“誤攔截”的情況,讓系統有更高的可用性和安全性。

本文中提到的方法只是眾多方法中的一種,我個人也是通過這種方式來實現的防止表單重復提交。當然了,我們可以選擇的辦法有很多,比如采用 JS 禁用提交按鈕、利用Session防止表單重復提交等等,最終如何選擇還是要根據自己的應用和開發框架來決定,選擇一個適合自己的方法才是最好的。

分享到:
標簽:Spring Boot
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定