Form anti-duplication submission

Source: Internet
Author: User

Prevent forms from repeating commits

This article describes the use of redirect technology to prevent form submissions, but redirect does not resolve to back to form pages when repeating the form, in order to solve this problem, added token mechanism. If you write the token generation and checksum code in each of the form-related processing methods, it is not acceptable in the actual project, and the next step is to use interceptors to generate and validate tokens.

1. General prevention of form repeat submission process:
    1. GET Access Form page
    2. Fill out the form
    3. POST Submission Form
    4. Server-side processing of form data, such as writing data to a database
    5. Redirect to another page to prevent users from refreshing the page by repeating the form

Result.htm
Result: ${result!}
User-form.htm

<! DOCTYPE html>



Parametercontroller

Package Controller;import Org.springframework.stereotype.controller;import Org.springframework.web.bind.annotation.requestmapping;import Org.springframework.web.bind.annotation.requestmethod;import Org.springframework.web.bind.annotation.requestparam;import org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controllerpublic class Parametercontroller {// Show Form @RequestMapping (value = "/user-form", method= requestmethod.get) public String Showuserform () {return '    User-form.htm "; }//Update User, save operation result to Redirectattributes,//Redirect to result page to display operation result @RequestMapping (value = "/user-form", met hod= requestmethod.post) public string handleuserform (@RequestParam string username, @R Equestparam String Password, final redirectattributes redirectattributes) {//UPDA        Te user in database ...        System.out.println ("Username:" + Username + ", Password:" + Password); The operation results are displayed toUser Redirectattributes.addflashattribute ("result", "the user is already successfully updated"); return "Redirect:/result"; URI instead of ViewName}//Display form processing result @RequestMapping ("/result") public String result () {return ' re    Sult.htm "; }}



Test A
    1. Visit http://localhost/user-form
    2. Click Update User to be redirected to the result page after the form is successfully submitted
    3. Refresh the result page and the form is not repeated, enabling the ability to prevent forms from repeating submissions
2. Use tokens to further enhance the prevention of duplicate submissions of forms

However, if you click the Back button in the browser to go back to the form page, click Update User and the form is submitted again. You can use token to prevent the back of the case of repeated submission of the form, access to the form page when you generate a token in the form and store the token on the server side, submit the form to check if the server has this token, if there is a first time to submit the form , then the token is removed from the server side, processing the form, redirect to the result page, if the server side does not have this token, it is a duplicate submitted form, does not handle the form of the submission.

Result.htm
Result: ${result!}
User-form.htm

Add a input field to the form to store tokens.

<! DOCTYPE html>



Parametercontroller

Package Controller;import Org.springframework.stereotype.controller;import Org.springframework.ui.modelmap;import Org.springframework.web.bind.annotation.requestmapping;import Org.springframework.web.bind.annotation.requestmethod;import Org.springframework.web.bind.annotation.requestparam;import Org.springframework.web.servlet.mvc.support.redirectattributes;import Javax.servlet.http.httpsession;import Java.util.UUID; @Controllerpublic class Parametercontroller {//Display form @RequestMapping (value = "/user-form", method= R equestmethod.get) public String showuserform (Modelmap model, HttpSession session) {String token = Uuid.randomuu        ID (). toString (). toUpperCase (). ReplaceAll ("-", "" ");        Model.addattribute ("token", token);        Session.setattribute (token, token);    return "user-form.htm"; }//Update User, save operation result to Redirectattributes,//Redirect to result page to display operation result @RequestMapping (value = "/user-form", met hod= requestmethod.post) Public String handleuserform (@Requestparam string username, @RequestParam string password, @RequestParam String Token, HttpSession session, Redire Ctattributes redirectattributes) {//Before processing the form, see if token is valid if (token = = NULL | | token.isempty () | |!token.equ        ALS (Session.getattribute (token))) {throw new RuntimeException ("Duplicate submission form");        }//Normal submission form, delete token session.removeattribute (token);        Update User in database ...        System.out.println ("Username:" + Username + ", Password:" + Password);        The result of the operation is displayed to the user redirectattributes.addflashattribute ("result", "the user is already successfully updated");    return "Redirect:result";    }//Displays the form processing result @RequestMapping ("/result") public String result () {return "result.htm"; }}



Test Two
    1. Visit http://localhost/user-form
    2. Click Update User to be redirected to the result page after the form is successfully submitted
    3. Refresh the result page and the form is not repeated, enabling the ability to prevent forms from repeating submissions
    4. Click the Back button to return to the form page, click Update User, because the token does not exist, the program throws an exception, prevent the form of repeated submissions (Display exception page is not the best way, more friendly way is to display a form duplicate submission prompt page)
3. Generate and verify tokens using the SPRINGMVC interceptor

Think about, in order to add token to the User-form, in the method of handling User-form new add a lot of code, if there are 10 form, do you want to use a token mechanism? Do you want to add so much code to each of the form-handling methods? The above token uses the UUID, if you want to change to a static type of integer, each generation will add 1? Tokens are stored in the session, and when the project is made to a certain extent, it is stored in a third-party cache such as Redis? Do you want to modify all of the form's processing methods for each requirement change? The workload is too large, who encountered such a problem will be crazy, no wonder the recruitment emphasizes: not to hit the project manager!

Fortunately, SPRINGMVC provides a mechanism for interceptors that can simply add token to a form
    • When accessing the User-form page, tokens generated in the Posthandle () of the interceptor are stored in the Modelandview and session.
    • When the form is submitted, the token is checked in the Interceptor's Prehandle (), and if token is invalid, the form is blocked from being submitted.
    • The form that needs to increase the token mechanism adds the URI of the form to the interceptor configuration.
    • If the form does not require a token mechanism, remove its URI from the interceptor's configuration.
    • You do not need to modify the code in the Controller.

What is token? Simple is the identification of an operation, can be a number, a string, and even objects, as long as the different forms can be submitted to distinguish it. A token is generated when the form is requested, and tokens are deleted after the form is submitted.

Result.htm
Result: ${result!}
User-form.htm

Add a input field to the form to store tokens.

<! DOCTYPE html>



Parametercontroller

As with the start Controller code, there is no token-related code.

Package Controller;import Org.springframework.stereotype.controller;import Org.springframework.web.bind.annotation.requestmapping;import Org.springframework.web.bind.annotation.requestmethod;import Org.springframework.web.bind.annotation.requestparam;import org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controllerpublic class Parametercontroller {// Show Form @RequestMapping (value = "/user-form", method= requestmethod.get) public String Showuserform () {return '    User-form.htm "; }//Update User, save operation result to Redirectattributes,//Redirect to result page to display operation result @RequestMapping (value = "/user-form", met hod= requestmethod.post) public string handleuserform (@RequestParam string username, @R Equestparam String Password, final redirectattributes redirectattributes) {//UPDA        Te user in database ...        System.out.println ("Username:" + Username + ", Password:" + Password); The operation results are displayed toUser Redirectattributes.addflashattribute ("result", "the user is already successfully updated");    return "Redirect:result";    }//Displays the form processing result @RequestMapping ("/result") public String result () {return "result.htm"; }}



Tokenvalidator

The Interceptor Tokenvalidator is used to generate and verify tokens.

Package Interceptor;import Org.springframework.web.servlet.handlerinterceptor;import Org.springframework.web.servlet.modelandview;import Javax.servlet.http.httpservletrequest;import Javax.servlet.http.httpservletresponse;import Java.util.uuid;public class Tokenvalidator implements Handlerinterceptor {public Boolean prehandle (HttpServletRequest request, Httpservletrespon        SE response, Object handler) throws Exception {//POST, PUT, DELETE request all may be form submissions if (! "            GET ". Equalsignorecase (Request.getmethod ())) {String Clienttoken = Request.getparameter (" token ");            String servertoken = (string) request.getsession (). getattribute (Clienttoken); if (Clienttoken = = NULL | | clienttoken.isempty () | |!clienttoken.equals (servertoken)) {throw new Runtimeex            Ception ("Duplicate submission form");        }//Normal submission form, delete token request.getsession (). removeattribute (Clienttoken);    }    return true;                           } public void Posthandle (HttpServletRequest request, httpservletresponse response, Object handler, Modelandview Modelandview) throws Exception {//GET request access Form page if (! "        GET ". Equalsignorecase (Request.getmethod ())) {return;  }//Generate token to store into session, and save to form input field String token = Uuid.randomuuid (). toString (). ReplaceAll ("-",        ""). toUpperCase ();        Modelandview.addobject ("token", token);    Request.getsession (). SetAttribute (token, token);                                } public void Aftercompletion (HttpServletRequest request, httpservletresponse response, Object handler, Exception ex) throws Exception {}}



Spring-mvc.xml in the Configuration interceptor

<beans> ...    <mvc:interceptors>        <mvc:interceptor>            <mvc:mapping path= "/user-form"/> <!--need to add tokens Check the form of the uri-->            <bean class= "Interceptor. Tokenvalidator "></bean>        </mvc:interceptor>    </mvc:interceptors></beans>



Test Three
    1. Visit http://localhost/user-form
    2. Click Update User to be redirected to the result page after the form is successfully submitted
    3. Refresh the result page and the form is not repeated, enabling the ability to prevent forms from repeating submissions
    4. Click the Back button to return to the form page, click Update User, because the token does not exist, the program throws an exception, prevent the form of repeated submissions (Display exception page is not the best way, more friendly way is to display a form duplicate submission prompt page)
The mechanism for adding tokens through SPRINGMVC interceptors,
    • Want to change the token generation strategy? Modify Tokenvalidator
    • Want to change token's storage strategy? Modify Tokenvalidator
    • Want to add a token check to a form? Modifying the configuration of the Spring-mvc.xml interceptor
    • Want to remove the token check for a form? Modifying the configuration of the Spring-mvc.xml interceptor
    • Do not need to modify any form processing method, Tarzan collapsed in front and color unchanged, the storm suddenly and calmly, the project manager seems not so hateful
Tips:

Token storage needs to consider expiration time, otherwise access 100,000 Times user-form page, generate 100,000 tokens without submitting the form, token has not been deleted, will cause a lot of waste of resources.

Token should be written to the hidden field of the form, in order to be intuitive, we write to the normal input:
<input type= "hidden" name= "token" value= "${token!}" >

The SPRINGMVC interceptor generates and verifies tokens, but can also be implemented using techniques such as the Servlet's filter.

Repeating the form should not directly display the exception to the user, you can use the SPRINGMVC exception handling mechanism, different pages display different exception friendly information

Form anti-duplication submission

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.