1. 程式人生 > >MVC_防止HttpPost重複提交

MVC_防止HttpPost重複提交

重複提交的場景很常見,可能是當時伺服器延遲的原因,如購物車物品疊加,重複提交多個訂單。常見的解決方法是提交後把Button在客戶端Js禁用,或是用Js禁止後退鍵等。在ASP.NET MVC 3 Web Application中 如何去防止這類HTTP-Post的重複提交呢? 我們可以藉助Session,放置一個Token在View/Page上,然後在Server端去驗證是不是同一個Token來判斷此次Http-Post是否有效。看下面的程式碼:  首先定義一個介面,便於擴充套件。

public interface IPageTokenView
{
    /// <summary>
    /// Generates the page token.
    /// </summary>
    string GeneratePageToken();

    /// <summary>
    /// Gets the get last page token from Form
    /// </summary>
    string GetLastPageToken { get; }

    /// <summary>
    /// Gets a value indicating whether [tokens match].
    /// </summary>
    /// <value>
    ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
    /// </value>
    bool TokensMatch { get; }
}


定義一個Abstract Class,包含一個

public abstract class PageTokenViewBase : IPageTokenView
{
    public static readonly string HiddenTokenName = "hiddenToken";
    public static readonly string SessionMyToken = "Token";

    /// <summary>
    /// Generates the page token.
    /// </summary>
    /// <returns></returns>
    public abstract string GeneratePageToken();

    /// <summary>
    /// Gets the get last page token from Form
    /// </summary>
    public abstract string GetLastPageToken { get; }

    /// <summary>
    /// Gets a value indicating whether [tokens match].
    /// </summary>
    /// <value>
    ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
    /// </value>
    public abstract bool TokensMatch { get; }
  
}


接著是實現SessionPageTokenView型別,記得需要在驗證通過後生成新的Token,對於這個Class是把它放到Session中。

    public class SessionPageTokenView : PageTokenViewBase
    {
        #region PageTokenViewBase

        /// <summary>
        /// Generates the page token.
        /// </summary>
        /// <returns></returns>
        public override string GeneratePageToken()
        {
            if (HttpContext.Current.Session[SessionMyToken] != null)
            {
                return HttpContext.Current.Session[SessionMyToken].ToString();
            }
            else
            {
                var token = GenerateHashToken();
                HttpContext.Current.Session[SessionMyToken] = token;
                return token;
            }
        }

        /// <summary>
        /// Gets the get last page token from Form
        /// </summary>
        public override string GetLastPageToken
        {
            get
            {
                return HttpContext.Current.Request.Params[HiddenTokenName];
            }
        }

        /// <summary>
        /// Gets a value indicating whether [tokens match].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [tokens match]; otherwise, <c>false</c>.
        /// </value>
        public override bool TokensMatch
        {
            get
            {
                string formToken = GetLastPageToken;
                if (formToken != null)
                {
                    if (formToken.Equals(GeneratePageToken()))
                    {
                        //Refresh token
                        HttpContext.Current.Session[SessionMyToken] = GenerateHashToken();
                        return true;
                    }
                }
                return false;
            }
        }

        #endregion 

        #region Private Help Method
        /// <summary>
        /// Generates the hash token.
        /// </summary>
        /// <returns></returns>
        private string GenerateHashToken()
        {
            return Utility.Encrypt(
                HttpContext.Current.Session.SessionID + DateTime.Now.Ticks.ToString());
        } 
        #endregion



這裡有到一個簡單的加密方法,你可以實現自己的加密方法. 

public static string Encrypt(string plaintext)
{
    string cl1 = plaintext;
    string pwd = string.Empty;
    MD5 md5 = MD5.Create();
    byte[] s = md5.ComputeHash(Encoding.Unicode.GetBytes(cl1));
    for (int i = 0; i < s.Length; i++)
    {
        pwd = pwd + s[i].ToString("X");
    }
    return pwd;
}


我們再來編寫一個Attribute繼承FilterAttribute, 實現IAuthorizationFilter介面。然後比較Form中Token與Session中是否一致,不一致就Throw Exception. Tips:這裡最好使用依賴注入IPageTokenView型別,增加Logging 等機制 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ValidateReHttpPostTokenAttribute : FilterAttribute, IAuthorizationFilter
{
    public IPageTokenView PageTokenView { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="ValidateReHttpPostTokenAttribute"/> class.
    /// </summary>
    public ValidateReHttpPostTokenAttribute()
    {
        //It would be better use DI inject it.
        PageTokenView = new SessionPageTokenView();
    }

    /// <summary>
    /// Called when authorization is required.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (!PageTokenView.TokensMatch)
        {
            //log...
            throw new Exception("Invaild Http Post!");
        }
      
    }
}


還需要一個HtmlHelper的擴充套件方法:

public static HtmlString GenerateVerficationToken(this HtmlHelper htmlhelper)
{
    string formValue = Utility.Encrypt(HttpContext.Current.Session.SessionID+DateTime.Now.Ticks.ToString());
    HttpContext.Current.Session[PageTokenViewBase.SessionMyToken] = formValue;

    string fieldName = PageTokenViewBase.HiddenTokenName;
    TagBuilder builder = new TagBuilder("input");
    builder.Attributes["type"] = "hidden";
    builder.Attributes["name"] = fieldName;
    builder.Attributes["value"] = formValue;
    return new HtmlString(builder.ToString(TagRenderMode.SelfClosing));
}


將輸出這類的HtmlString: 

<input name="hiddenToken" type="hidden" value="1AB01826F590A1829E65CBD23CCE8D53" />


我們建立一個叫_ViewToken.cshtml的Partial View,這樣便於模組化,讓我們輕易加入到具體View裡,就兩行程式碼,第一行是擴充套件方法NameSpace

@using Mvc3App.Models;
@Html.GenerateVerficationToken()
假設我們這裡有一個簡單的Login.cshtml,然後插入其中:
   <form method="post" id="form1" action="@Url.Action("Index")">
    <p>
        @Html.Partial("_ViewToken")
        UserName:<input type="text" id="fusername" name="fusername" /><br />
        Password:<input type="password" id="fpassword" name="fpassword" />
        <input type="submit" value="Sign-in" />
    </p>
    </form>

這裡我們Post的Index Action,看Controller程式碼,我們在Index上加上ValidateReHttpPostToken的attribute.


[HttpPost]
[ValidateReHttpPostToken]
public ActionResult Index(FormCollection formCollection)
{
    return View();
}

public ActionResult Login()
{
    return View();
}

相關推薦

MVC_防止HttpPost重複提交

重複提交的場景很常見,可能是當時伺服器延遲的原因,如購物車物品疊加,重複提交多個訂單。常見的解決方法是提交後把Button在客戶端Js禁用,或是用Js禁止後退鍵等。在ASP.NET MVC 3 Web Application中 如何去防止這類HTTP-Post的重複提交呢? 我們可以藉助Session,放置一

防止訂單重複提交的幾種方法

第一種辦法: var flag = false;// 在提交函式外面定義個變數防止重複提交   //提交函式 function sublimit() {     if(flag){      

客戶端 防止訂單重複提交 介面處理

如果是瀏覽器,可以採用session的方法 具體的做法:在伺服器端生成一個唯一的隨機標識號,專業術語稱為Token(令牌),同時在當前使用者的Session域中儲存這個Token。然後將Token傳送到客戶端的Form表單中,在Form表單中使用隱藏域來儲存這個Token,表單提交的時候連同這個

javaEE開發中使用session同步和token機制來防止併發重複提交

    通常在普通的操作當中,我們不需要處理重複提交的,而且有很多方法來防止重複提交。比如在登陸過程中,通過使用redirect,可以讓使用者登陸之上重定向到後臺首頁介面,當用戶重新整理介面時就不會觸發重複提交了。或者使用token,隱藏在表單中,當提交時進行token驗證,

jsp防止按鈕重複提交

使用jquery方法: <html> <body> <form method="get" action="" target="box"> <input

如何防止使用者重複提交

在我們的應用系統中,禁止使用者做出多次提交的操作是非常有必要的,比如在B2C這樣的系統裡,假如某頁面有個去銀行結算的提交按鈕,可能因為系統或網路延遲,當用戶點選提交後,返回給使用者的依舊是未付款或等待

使用struts的token防止頁面重複提交

/* * Generated by MyEclipse Struts * Template path: templates/java/JavaClass.vtl */ package com.sunsoft.struts.action; import javax.se

防止使用者重複提交的方法

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

防止頁面重複提交及後退方法

1.提交後禁用提交按鈕(大部分人都是這樣做的) 如果客戶提交後,按F5重新整理怎麼辦? 2.使用Session 在提交的頁面也就是資料庫處理之前: 資料處理完後,修改session("ok")=false。 3.資料處理成功馬上Redirect到另外一個頁面 操作後重新

防止頁面重複提交(使用UUID--Session)

uuid.jsp: <%@ page language="java" import="java.util.*,day22.UuidToken" pageEncoding="UTF-8"%> <html> <head> <

頁面按鈕防止ajax重複提交

這兩天在做一個專案,頁面互動非常多,到處都是ajax,測試時發現點選按鈕是很有可能重複請求ajax,看網上說加鎖的方法,不過我也有一個方法,不知道別人用過沒,廢話不說,我用了CSS中的pointer-events屬性,直接上JS程式碼吧; ` var

表單防止頁面重複提交方法

使用者在操作表單資料時往往會出現表單資料重複提交的問題,尤其實在Web開發中此類問題比較常見。重新整理頁面,後退操作以前的頁面,單機多次按鈕都會導致資料重複提交。此類問題是因為瀏覽器重複提交HTTP請求導致。    下面簡單介紹以防止表單資料重複提交的四種解決方案。 一、在

客戶端服務端防止使用者重複提交表單

一、什麼是表單重複提交? 當網路有延遲時,使用者提交的表單等資料還沒有完成此次提交,但使用者又多次點選提交,造成使用者資料在資料庫或儲存中被提交多次。 利用執行緒延遲,簡單模擬重複提交。 表單頁面為form.html [html] view plain

防止資料重複提交的6種方法(超簡單)!

有位朋友,某天突然問磊哥:**在 Java 中,防止重複提交最簡單的方案是什麼**? 這句話中包含了兩個關鍵資訊,第一:**防止重複提交**;第二:**最簡單**。 於是磊哥問他,是單機環境還是分散式環境? 得到的反饋是單機環境,那就簡單了,於是磊哥就開始裝*了。 話不多說,我們先來複現這個問

利用session防止表單重複提交

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

springMvc 攔截器 防止重複提交

1.DispatcherServlet     SpringMVC具有統一的入口DispatcherServlet,所有的請求都通過DispatcherServlet。     DispatcherServlet是前置控

Http協議與表單防止重複提交實戰解決方案

http長連線與短連線 HTTP協議與TCP/IP協議的關係 HTTP的長連線和短連線本質上是TCP長連線和短連線。HTTP屬於應用層協議,在傳輸層使用TCP協議,在網路層使用IP協議。IP協議主要解決網路路由和定址問題,TCP協議主要解決如何在IP層之上可靠的傳遞資料包

自定義註解 防止重複提交

定義註解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.a

java防止頁面重新整理重複提交

轉自:https://blog.csdn.net/JasonSSH/article/details/7528539 看了網上的,有幾種方法:  1 在你的表單頁裡HEAD區加入這段程式碼:  <META HTTP-EQUIV="pragma" CONTENT="no-c

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

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