1. 程式人生 > >SpringBoot攔截器+註解方式實現防止表單重複提交

SpringBoot攔截器+註解方式實現防止表單重複提交

表單重複提交在web應用中是比較常見的問題,重複提交的動作容易造成髒資料,為了避免這重複提交的操作簡便的方便是採用攔截器+註解的方式。

基本的原理:

url請求時,用攔截器攔截,生成一個唯一的識別符號(token),在新建頁面中Session儲存token隨機碼,當儲存時驗證,通過後刪除,當再次點選儲存時由於伺服器端的Session中已經不存在了,所有無法驗證通過。

第一步:自定義註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidDuplicateSubmission
{
boolean needSaveToken() default false; boolean needRemoveToken() default false; String tokenName() default "token"; }

元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation型別,它們被用來提供對其它 annotation型別作說明。Java5.0定義的元註解:

[email protected]

[email protected]

3.Documented

[email protected]

@Target:

作用:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)

取值(ElementType)有:

    1.CONSTRUCTOR:用於描述構造器
    2.FIELD:用於描述域
    3.LOCAL_VARIABLE:用於描述區域性變數
    4.METHOD:用於描述方法
    5.PACKAGE:用於描述包
    6.PARAMETER:用於描述引數
    7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告

@Retention:

作用:表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)

取值(RetentionPoicy)有:

    1.SOURCE:在原始檔中有效(即原始檔保留)
    2.CLASS:在class檔案中有效(即class保留)
    3.RUNTIME:在執行時有效(即執行時保留)

@Inherited:
@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面,由編譯程式自動完成其他細節。在定義註解時,不能繼承其他的註解或介面。@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別(返回值型別只能是基本型別、Class、String、enum)。可以通過default來宣告引數的預設值。

定義註解格式:
  public @interface 註解名 {定義體}

Annotation型別裡面的引數該怎麼設定:
  第一,只能用public或預設(default)這兩個訪問權修飾.例如,String value();這裡把方法設為defaul預設型別;   
  第二,引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和 String,Enum,Class,annotations等資料型別,以及這一些型別的陣列.例如,String value();這裡的引數成員就為String;  
  第三,如果只有一個引數成員,最好把引數名稱設為”value”,後加小括號.例:下面的例子FruitName註解就只有一個引數成員。
 
 第二步:實現攔截器
 

public class AvoidDuplicateSubmissionInterceptor extends HandlerInterceptorAdapter {

    private static final Logger logger = LogManager.getLogger(AvoidDuplicateSubmissionInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HttpSession session = request.getSession(false);
        Object userId = session == null ? null : session.getAttribute("userId");
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        AvoidDuplicateSubmission annotation = method.getAnnotation(AvoidDuplicateSubmission.class);
        if (annotation != null) {
            boolean needSaveSession = annotation.needSaveToken();
            String tokenName = annotation.tokenName();
            if (needSaveSession) {
                request.getSession().setAttribute(tokenName, TokenProccessor.getInstance().makeToken(userId == null ? "" : userId.toString()));
            }

            boolean needRemoveSession = annotation.needRemoveToken();
            if (needRemoveSession) {
                if (isRepeatSubmit(request, tokenName)) {
                    logger.warn("please don't repeat submit,[user:" + userId + ",url:" + request.getServletPath() + "]");
                    ResponseBody responseBody = method.getAnnotation(ResponseBody.class);
                    if (responseBody == null) {
                        response.sendRedirect("repeatsubmit.htm");
                    }
                    return false;
                }
                request.getSession(false).removeAttribute(tokenName);
            }
        }

        return true;
    }

    private boolean isRepeatSubmit(HttpServletRequest request, String tokenName) {
        String serverToken = (String) request.getSession(false).getAttribute(tokenName);
        if (serverToken == null) {
            return true;
        }
        String clientToken = request.getParameter(tokenName);
        if (clientToken == null) {
            return true;
        }
        if (!serverToken.equals(clientToken)) {
            return true;
        }
        return false;
    }

第三步:在springBoot的配置類中加入攔截器

@Configuration
public class StartupConfig extends WebMvcConfigurerAdapter {
    private static final Logger logger = LogManager.getLogger(StartupConfig .class);

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AvoidDuplicateSubmissionInterceptor()).addPathPatterns("/*.htm");
    }
}   

第四步:在相應的controller方法加上註解

@AvoidDuplicateSubmission(needSaveToken = true)
    @RequestMapping(value = "/insertXXX.htm")
    @ResponseBody
    public String insertXXX(Grade grade, HttpServletRequest req, HttpServletResponse resp) {
        JSONObject obj = new JSONObject();
        obj.put("success", true);
        try {

            String message = validateInsertForm(membershipGrade);
            if (StringUtils.isBlank(message)) {
                String userId = req.getSession().getAttribute("userId").toString();
                String userName = req.getSession().getAttribute("realName").toString();
                grade.setId(String.valueOf(SnowFlake.getId()));
                grade.setCreateUser(userId);
                grade.setCreateUserName(userName);
                grade.setCreateTime(DateUtil.formatDatetime(new Date(), "yyyyMMddHHmmss"));
                gradeService.insert(membershipGrade);
                obj.put("message", "新增成功");
            } else {
                obj.put("success", false);
                obj.put("message", message);
            }
        } catch (Exception e) {
            obj.put("success", false);
            obj.put("message", "系統錯誤");
            logger.error(e.getMessage(), e);
        }
        return obj.toString();
    }

最後在頁面的form中加入下面的,就防止重複提交的問題

<input type="hidden" name="token" value="${token}">

下面是生成token的方法

public class TokenProccessor {
    private static final Logger logger = LogManager.getLogger(TokenProccessor.class);

    private TokenProccessor() {
    }

    private static final TokenProccessor instance = new TokenProccessor();

    public static TokenProccessor getInstance() {
        return instance;
    }

    public String makeToken(String userId) {
        String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + userId;
        try {
            return DigestUtils.md5Hex(token);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return System.currentTimeMillis() + "";
    }
}

相關推薦

SpringBoot攔截+註解方式實現防止重複提交

表單重複提交在web應用中是比較常見的問題,重複提交的動作容易造成髒資料,為了避免這重複提交的操作簡便的方便是採用攔截器+註解的方式。 基本的原理: url請求時,用攔截器攔截,生成一個唯一的識別符號(token),在新建頁面中Session儲存toke

Spring MVC攔截+註解方式實現防止重複提交

表單重複提交是在多使用者Web應用中最常見、帶來很多麻煩的一個問題。有很多的應用場景都會遇到重複提交問題,比如: 1.點選提交按鈕兩次。2.點選重新整理按鈕。3.使用瀏覽器後退按鈕重複之前的操作,導致重複提交表單。4.使用瀏覽器歷史記錄重複提交表單。5.瀏覽器重複的HTTP

利用Spring AOP和redis的鎖來實現防止重複提交

表單重複提交是在web中存在的一個很常見,會帶來很多麻煩的一個問題。尤其是在表單新增的時候,如果重複提交了多條一樣的資料,帶來的麻煩更大。 實現防止表單重複提交的方法有前端限制和後臺限制1、前端限制就是當點選了提交按鈕之後,就給按鈕新增屬性disabled,然後等後臺返回提交

springboot攔截註解方式

WebMvcConfigurerAdapter配置類其實是Spring內部的一種配置方式,採用JavaBean的形式來代替傳統的xml配置檔案形式進行鍼對框架個性化定製,下面我們來看一下該類內的常用方法。 本章目標 繼承WebMvcConfigurerAdapter採用Ja

自定義註解攔截,防止重複提交

1.自定義註解 package com.paotui.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import ja

JFinal的攔截防止重複提交

       前段時間師傅讓我整理一份防止表單重複提交的方案,平常所用的提交按鈕後通過js禁止提交等方式也可以的,若是禁用js可能就尷尬了,不過也沒幾個人會這麼幹。起初也是在網上百度各種資料照貓畫虎搞一套的,畢竟目前水平低低的我確實需要多學習,先不說創造,乖乖吸取前輩們的經驗

struts2-註解&防止重複提交

註解: 註解沒有分號 註解首字母是大寫,因為註解與類、介面是同一級的。一個註解後臺對應一個@interface類 同一語法單元,同一註解只能使用一次 在註解與語法單元間可以隔若干空行、註釋等非程式碼內容 在struts2中使用註解,主要完成對Act

Struts2中防止重複提交的兩種方式

防止表單重複提交,這是個很重要的知識點,而且很有用。當用戶提交了一個表單,此時,位址列顯示的是處理這個表單的Action的地址,若此時重新整理,則會重新發送一次表單資料,即又進行了一次提交,若這個Action是用來處理使用者註冊的,那麼重複提交會再一次向資料庫中插入之前已

利用session防止重複提交

使用者在提交表單的過程中,由於網路等原因,可能重複點選提交按鈕,向資料庫重複寫入或者讀取資料,為了防止這種情況發生。   解決方式: 1.客戶端防表單重複提交,在前端使用javascript限制。但是在前端並不能完全限制,比如下網頁原始碼更改,重複重新整理等。 2.服務端防

Java 使用Token令牌防止重複提交

Token驗證詳解 參考來源:https://blog.csdn.net/woshihaiyong168/article/details/52857479 使用Token令牌防止表單重複提交 參考來源:https://blog.csdn.net/cuiyaoqiang/article/d

用session防止重複提交

session案例1:防止表單重複提交   原理:     1,表單頁面由servlet程式生成,servlet為每次產生的表單頁面分配一個唯一的隨機標識號,並在FORM表單的一個隱藏欄位中設定這個標識號,同時在當前使用者的Session域中儲存這個標識號。

防止重複提交的幾種方法總結

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/Warpar/article/details/72917924 1、JavaScript防止表單重複提交(主要用於網路延遲情況下使用者點選多次submit按鈕導致表單重複提

防止重複提交---筆記

1. 防止表單重複提交 1.在使用者訪問頁面(設為頁面A)時session設定一個屬性(設為check) 值為 md5(當前時間)設為checkvalue, 且在表單中設定隱藏域 value為checkvalue 2.當用戶提交 在servlet裡檢測se

防止重複提交

問題:什麼是表單重複提交?                     regist.jsp----->RegistServlet         表單重複提交 危害: 刷票、 重複註冊、帶來伺服器訪問壓力(拒絕服務)                  解決方案:     

Jsp 防止重複提交幾種方案

SP避免Form重複提交的三種方案  1) javascript ,設定一個變數,只允許提交一次。    <script language="javascript">    var checksubmitflg = false;    function

session案例:防止重複提交、一次性校驗碼

session案例1:防止表單重複提交 原理: 1,表單頁面由servlet程式生成,servlet為每次產生的表單頁面分配一個唯一的隨機標識號,並在FORM表單的一個隱藏欄位中設定這個標識號,同時在當前使用者的Session域中儲存這個標識號。 2,當用戶提交FOR

laravel中防止重複提交的綜合解決方案

怎樣防止表單重複提交,通過搜尋引擎能搜到很多結果,但很零散,系統性不強,正好前幾天做了這個功能,決定記錄下來。 根據資料流向的過程,分別在三個“點”控制表單的重複提交,如下: 第一,使用者觸發submit時,前端js控制提交按鈕的狀態,使用者觸發提交即設

SSH框架之Struts的常用技術——資料回顯、防止重複提交

Struts2的常用三大技術: 1、資料回顯 2、模型驅動 3、防止表單重複提交 一、資料回顯: 1、資料回顯,必須要用struts標籤! 2、程式碼講解: 1)Action: //

防止重複提交的幾種策略

表單重複提交在客戶端和伺服器端都會引發一些問題,一方面有些瀏覽器會彈出確認是否確認重新提交表單,另一方面伺服器端也可能會重複新增資料。 在瀏覽器端,當用戶點選提交一個表單,而伺服器沒有對提交做重定向,當用戶輸入資訊有誤提交後依然返回的是提交的頁面,或者在同一個頁面顯示提交成

PHP防止重複提交方法

下面的情況就會導致表單重複提交:       點選提交按鈕兩次。       點選重新整理按鈕。       使用瀏覽器後退按鈕重複之前的操作,導致重複提交表單。       使用瀏覽器歷史記錄重複提交表單。       瀏覽器重複的HTTP請求。