Form submission details

Keywords: Java Session Apache log4j

Common application scenarios for form duplicate submission
1. When the network is delayed, the user clicks the submit button several times, which leads to the delay.
2. After submitting the form, the user clicks the refresh button to cause the form to be submitted repeatedly.
3. After submitting the user's form, click the browser Back button to return to the form page and submit again.

In many cases, duplicate submission of data is not what we want, such as the submission of orders, submission of applications for refunds, and so on, so how to prevent re-submission?

The following four methods are introduced, the last one being emphasized:

1. Adding uniqueness constraints to fields required by the database

This method is the most effective way to prevent data duplicate submission, but there will still be duplicate submission in the front desk, and backstage returns will be wrong.

2. The front end uses js to set disable after clicking the button to submit, or js to set an attribute, which is true before submission and false after submission.
Client disables js, this method will not work

3. Use Post/Redirect/Get

Post/Redirect/Get, referred to as PRG, is a Web design pattern that can prevent the repeated submission of form data. The typical problems of repeated submission of form data, such as user refresh submission response pages, can be avoided by PRG pattern. For example, when the user submits successfully, the client redirects and jumps to the successful submission page.

Note: PRG design patterns do not apply to all duplicate submissions, such as:

1) duplicate submission caused by user refreshing submission POST request due to slow response of server.

2) The user clicks the back button and returns to the data submission interface, resulting in repeated data submission.

3) Users repeatedly click the submit button, resulting in data duplicate submission.

4) Users maliciously avoid multiple submissions by the client and submit duplicate data.

4. Setting tokens with session s and annotations

The so-called token is actually a kind of sign, marking the current submission status. For example, in the ancient wartime, when the emperor issued an order, he would carry a tiger symbol to the people under his command to convey it to the general, and the general also had the other half of the tiger symbol from the emperor. If the general could make a pair, he would fight according to the order conveyed. If the comer did not carry the tiger symbol, the general would definitely cut off the comer. Head.

So how to set this token, the following figure is a way of thinking to write this token:

1. Write a simple annotation class first.

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;

/**

  • Customize a Token annotation to identify methods that need to be resubmitted
  • @author ranger

*
*/
@Target(value=ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TokenForm {


//Create Token attributes for tagging methods that need to be avoided
boolean create() default false;
//Delete Token's attributes for marking up methods that need to be guarded against Re-mentioning
boolean remove() default false;

}

2. Create token before jumping to add pages

/**
 * Jump to Additional Page
 * Request path: ${pageContext.request.contextPath}/admin/toAdminAdd
 * @return
 */

@RequestMapping(value="/toAdminAdd")
@TokenForm(create=true)
public String toAdminAdd(HttpServletRequest request) {

    return "manager/adminAdd";
}

3. Delete token after adding data
/**

 * Added Administrator
 * Request path: ${pageContext.request.contextPath }/admin/addAdmin
 * @param admin
 * @param request
 * @return
 */
@RequestMapping(value="/addAdmin")
@TokenForm(remove=true)
public String addAdmin(@RequestParam Map<String, Object> admin,HttpServletRequest request) {
    try {
        //After encoding the password Md5, insert it
        admin.put("admin_pwd",Md5Utils.md5((String)admin.get("admin_pwd")) );
        
        LOGGER.debug("-Added Administrator-"+admin);
        adminService.addAdmin(admin);
        request.setAttribute("admin_add_msg", "Increase Administrator Success");
        
    } catch (Exception e) {
        e.printStackTrace();
        request.setAttribute("admin_add_msg", "Administrator Increase Failed");
    }
    return "manager/adminAdd";
}

4. Create an interceptor to intercept token information before sending path
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import cn.gzsxt.annotation.TokenForm;

public class TokenInterceptor implements HandlerInterceptor {


private static final Logger LOGGER = LogManager.getLogger(TokenInterceptor.class);

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    
    //Step 1: Obtain annotations for calling processing methods
    HandlerMethod hm=(HandlerMethod) handler;
    TokenForm tokenForm = hm.getMethodAnnotation(TokenForm.class);
    
    
    //Step 2: Determine whether there are Token annotations
    if (tokenForm!=null) {
        HttpSession session = request.getSession();
        if (tokenForm.create()==true) {
            session.setAttribute("token", UUID.randomUUID().toString());
            LOGGER.debug("Printed token:"+session.getAttribute("token"));
        }
        if (tokenForm.remove()==true) {
            //Determine whether the Token of the form is the same as the Token of the server
            String formToken = request.getParameter("token");
            Object sessionToken = session.getAttribute("token");
            //The Token passed in is the same as the Token on the server side, allowing operation and deleting session Token
            if (formToken.equals(sessionToken)){
                session.removeAttribute("token");
            }else{
                //Jump to the specified path
                String invoke = request.getParameter("token.invoke");
                response.sendRedirect(request.getContextPath()+invoke);
                return false;
            }
        }
        
    }
    
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        ModelAndView modelAndView) throws Exception {
    // TODO Auto-generated method stub
    
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
    // TODO Auto-generated method stub
    
}

5. The front-end specifies the token submitted and the address token.invoke that can be jumped after repeated submissions

<input type="hidden" name="token" value="${sessionScope.token }"
<input type="hidden" name="token.invoke" value="/admin/toAdminAdd">

Posted by seularts on Sun, 12 May 2019 04:03:03 -0700