日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取

發布時間:2025/3/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
@Time:2019年1月4日 16:19:19 @Author:QGuo 背景:最開始打算寫個防止表單重復提交的攔截器;網上見到一種不錯的方式,比較合適前后端分離,校驗在后臺實現;

?

我在此基礎上,將key,value。Objects.hashCode()了下 因為request的body 可能太大,過長; 但不保證存在不同的object生成的哈希值卻相同,但是我們目的只是為了防止重復提交而已,不同對象生成哈希值相同的機率很小。 ==========================代碼==============================

1、HttpServletRequestReplacedFilter 過濾器.

目的:post請求時,復制request;注意代碼中的注釋部分; package com.kdgz.service;import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException;/*** @author QGuo* @date 2019/1/3 15:04*/ public class HttpServletRequestReplacedFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {HttpServletRequest httpServletRequest = (HttpServletRequest) request;String contentType = request.getContentType();if (contentType != null && contentType.contains("application/x-www-form-urlencoded")) {//如果是application/x-www-form-urlencoded, 參數值在request body中以 a=1&b=2&c=3...形式存在,//若直接構造BodyReaderHttpServletRequestWrapper,在將流讀取并存到copy字節數組里之后,//httpRequest.getParameterMap()將返回空值!//若運行一下 httpRequest.getParameterMap(), body中的流將為空! 所以兩者是互斥的! request.getParameterMap();}if ("POST".equals(httpServletRequest.getMethod().toUpperCase())) {requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) request);}}if (requestWrapper == null) {chain.doFilter(request, response);} else {chain.doFilter((HttpServletRequest)requestWrapper, response);}}@Overridepublic void init(FilterConfig arg0) throws ServletException {} } HttpServletRequestReplacedFilter 2、HttpServletRequestWrapper --復制ServletRequest 目的在于:使servletRequest可以重復獲取inputStream、reader; 1 package com.kdgz.service; 2 3 import javax.servlet.ReadListener; 4 import javax.servlet.ServletInputStream; 5 import javax.servlet.http.HttpServletRequest; 6 import javax.servlet.http.HttpServletRequestWrapper; 7 import java.io.BufferedReader; 8 import java.io.ByteArrayInputStream; 9 import java.io.IOException; 10 import java.io.InputStreamReader; 11 import java.nio.charset.Charset; 12 import java.util.Enumeration; 13 import java.util.Map; 14 15 /** 16 * @author QGuo 17 * @date 2019/1/3 15:05 18 */ 19 public class BodyHttpServletRequestWrapper extends HttpServletRequestWrapper { 20 21 private byte[] body; 22 23 public byte[] getBody() { return body; } 24 25 public void setBody(byte[] body) { this.body = body; } 26 27 public BodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException { 28 super(request); 29 body = this.getBodyString(request).getBytes(Charset.forName("UTF-8")); 30 } 31 32 @Override 33 public BufferedReader getReader() throws IOException { 34 return new BufferedReader(new InputStreamReader(getInputStream(),"UTF-8")); 35 } 36 37 @Override 38 public ServletInputStream getInputStream() throws IOException { 39 40 final ByteArrayInputStream bais = new ByteArrayInputStream(this.body); 41 42 return new ServletInputStream() { 43 @Override 44 public boolean isFinished() { return false; } 45 46 @Override 47 public boolean isReady() { return false; } 48 49 @Override 50 public void setReadListener(ReadListener readListener) {} 51 52 @Override 53 public int read() throws IOException { return bais.read(); } 54 }; 55 } 56 57 @Override 58 public String getHeader(String name) { return super.getHeader(name); } 59 60 @Override 61 public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); } 62 63 @Override 64 public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); } 65 66 @Override 67 public Map<String, String[]> getParameterMap() { return super.getParameterMap(); } 68 69 public String getBodyString(ServletRequest request) { 70 StringBuilder sb = new StringBuilder(); 71 InputStream inputStream = null; 72 BufferedReader reader = null; 73 try { 74 inputStream = request.getInputStream(); 75 reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); 76 String line = ""; 77 while ((line = reader.readLine()) != null) { 78 sb.append(line); 79 } 80 } catch (IOException e) { 81 e.printStackTrace(); 82 } finally { 83 if (inputStream != null) { 84 try { 85 inputStream.close(); 86 } catch (IOException e) { 87 e.printStackTrace(); 88 } 89 } 90 if (reader != null) { 91 try { 92 reader.close(); 93 } catch (IOException e) { 94 e.printStackTrace(); 95 } 96 } 97 } 98 return sb.toString(); 99 } 100 } HttpServletRequestWrapper

?

3、web.xml 中添加過濾器

1 <filter> 2 <filter-name>httpServletRequestFilter</filter-name> 3 <filter-class>com.kdgz.service.HttpServletRequestReplacedFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>httpServletRequestFilter</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>

?

4、添加自定義注解

1 package com.kdgz.annotation; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 /** 9 * @author QGuo 10 * @date 2018/12/24 13:58 11 * 一個用戶 相同url 同時提交 相同數據 驗證 12 */ 13 @Target(ElementType.METHOD) 14 @Retention(RetentionPolicy.RUNTIME) 15 public @interface SameUrlData { 16 } @SameUrlData

?

5、添加攔截器

1 package com.kdgz.service; 2 3 import com.alibaba.fastjson.JSON; 4 import com.kdgz.annotation.SameUrlData; 5 import org.apache.commons.lang3.StringUtils; 6 import org.springframework.data.redis.core.StringRedisTemplate; 7 import org.springframework.web.method.HandlerMethod; 8 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 9 10 import javax.annotation.Resource; 11 import javax.servlet.http.HttpServletRequest; 12 import javax.servlet.http.HttpServletResponse; 13 import java.io.IOException; 14 import java.lang.reflect.Method; 15 import java.util.HashMap; 16 import java.util.Map; 17 import java.util.Objects; 18 import java.util.concurrent.TimeUnit; 19 20 /** 21 * 一個用戶 相同url 同時提交 相同數據 驗證 22 * 主要通過 session中保存到的url 和 請求參數。如果和上次相同,則是重復提交表單 23 * 24 * @author QGuo 25 * @date 2018/12/24 14:02 26 */ 27 public class SameUrlDataInterceptor extends HandlerInterceptorAdapter { 28 @Resource 29 StringRedisTemplate stringRedisTemplate; 30 31 @Override 32 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 33 if (handler instanceof HandlerMethod) { 34 HandlerMethod handlerMethod = (HandlerMethod) handler; 35 Method method = handlerMethod.getMethod(); 36 SameUrlData annotation = method.getAnnotation(SameUrlData.class); 37 if (annotation != null) { 38 if (repeatDataValidator(request)) {//如果重復相同數據 39 //在此可添加response響應內容,提醒用戶重復提交了 40 return false; 41 } else 42 return true; 43 } 44 return true; 45 } else { 46 return super.preHandle(request, response, handler); 47 } 48 } 49 50 /** 51 * 驗證同一個url數據是否相同提交 ,相同返回true 52 * 53 * @param httpServletRequest 54 * @return 55 */ 56 public boolean repeatDataValidator(HttpServletRequest httpServletRequest) throws IOException { 57 Map<String, String[]> parameterMap = new HashMap(httpServletRequest.getParameterMap()); 58 //刪除參數中的v;(v參數為隨機生成的字符串,目的是為了每次訪問都是最新值,既然要防止重復提交,需要剔除此參數) 59 if (parameterMap.containsKey("v")) 60 parameterMap.remove("v"); 61 //每一位登錄者都有唯一一個token認證 62 String tokens = ""; 63 if (parameterMap.get("token").length > 0) 64 tokens = parameterMap.get("token")[0]; 65 String method = httpServletRequest.getMethod().toUpperCase();//請求類型,GET、POST 66 String params; 67 if (StringUtils.equals(method, "POST")) {//post請求時 68 BodyHttpServletRequestWrapper requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) httpServletRequest); 69 byte[] bytes = requestWrapper.getBody(); 70 if (bytes.length != 0) { 71 params = JSON.toJSONString(new String(bytes, "UTF-8").trim()); 72 } else {//若body被清空,則說明參數全部被填充到Parameter集合中了 73 /** 74 * 當滿足一下條件時,就會被填充到parameter集合中 75 * 1:是一個http/https請求 76 * 2:請求方法是post 77 * 3:請求類型(content-Type)是application/x-www-form-urlencoded 78 * 4: Servlet調用了getParameter系列方法 79 */ 80 Map<String, String[]> map = new HashMap(requestWrapper.getParameterMap()); 81 // 去除 v 參數 82 if (map.containsKey("v")) 83 map.remove("v"); 84 params = JSON.toJSONString(map); 85 } 86 } else { 87 params = JSON.toJSONString(parameterMap); 88 } 89 90 String url = String.valueOf(Objects.hashCode(httpServletRequest.getRequestURI() + tokens)); 91 Map<String, String> map = new HashMap<String, String>(); 92 map.put(url, params); 93 //防止參數過多,string過大;現將儲存為 hash編碼; 94 String nowUrlParams = String.valueOf(Objects.hashCode(map)); 95 String preUrlParams = stringRedisTemplate.opsForValue().get(url); 96 if (preUrlParams == null) {//如果上一個數據為null,表示還沒有訪問頁面 97 //設置過期時間為3分鐘 98 stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES); 99 return false; 100 } else if (preUrlParams.equals(nowUrlParams)) {//否則,已經訪問過頁面 101 //如果上次url+數據和本次url+數據相同,則表示重復添加數據 102 return true; 103 } else {//如果上次 url+數據 和本次url加數據不同,則不是重復提交,更新 104 stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES); 105 return false; 106 } 107 } 108 } SameUrlDataInterceptor

?

使用的時候,只要在接口上,添加注解即可 例如: @RequestMapping(value = "v1.0/monGraphSave")
@SameUrlData
public AjaxMessage monGraphSave(@RequestBody MonGraphFB monGraphFB){} ====================代碼結束=================== 整理至此,主要有以下注意點; ①、得考慮post請求參數獲取的特殊性 ②、request.getInputStream() 只能獲取一次,要想可以多次讀取,得繼承HttpServletRequestWrapper,讀出來--放回去 ③、過濾器的目的是可以直接讀取request里面的body ④、request參數body可能很大,可以取hash值。 ⑤、key、value的存儲,需要設置過期時間; 心得: 其實我覺得防止表單重復提交這個功能,作用不是特別大;因為只要隨便加一個參數,就可以把需要的參數重復添加進系統中; 只能做到,防止用戶誤操作,點擊了多次這種情況;(一般前端也會做處理的,但萬一前端抽風自動發起了多次請求呢); 只能說一定程度上 更加完善吧 改進: 可以在SameUrlDataInterceptor攔截器中,添加response響應內容,讓用戶知道自己重復提交了。 很簡單不舉例了;

轉載于:https://www.cnblogs.com/QGuo/p/10234386.html

總結

以上是生活随笔為你收集整理的由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。