表單的重復(fù)提交
- 重復(fù)提交的情況:
①. 在表單提交到一個 Servlet,而 Servlet 又通過請求轉(zhuǎn)發(fā)的方式響應(yīng)了一個 JSP(HTML)頁面,此時地址欄還保留著 Servlet 的那個路徑,在響應(yīng)頁面點擊 “刷新”。
②. 在響應(yīng)頁面沒有到達時,重復(fù)點擊 “提交按鈕”
③. 點擊返回,再點擊提交
- 不是重復(fù)提交的情況:點擊 “返回”,“刷新” 原表單頁面,再點擊提交。
- 如何避免表單的重復(fù)提交:在表單中做一個標(biāo)記,提交到 Servlet 時,檢查標(biāo)記是否存在且和預(yù)定義的標(biāo)記一樣,若一致,則受理請求,并銷毀標(biāo)記,若不一致或沒有標(biāo)記,則直接響應(yīng)提示信息:“重復(fù)提交”
①僅提供一個隱藏域不行:<input type="hidden" name="token" value="lsy">
②把標(biāo)記放在 Request 中 , 行不通,表單頁面刷新后,request 已經(jīng)被銷毀,再提交表單是一個新的 request 的。
③把標(biāo)記放在 Session 中,可以
1. 在原表單頁面,生成一個隨機值 token
2. 在原表單頁面,把 token 值放入 session 屬性中
3. 在原表單頁面,把 token 值放入到隱藏域
4. 在目標(biāo)的 Servlet 中:獲取 session 和隱藏域中的 token 值
比較兩個值是否一致,受理請求,且把 session 域中的 token 屬性清除,若不一致,則直接響應(yīng)提示頁面:“重復(fù)提交”
我們可以通過 Struts1 中寫好的類 TokenProcessor 來重構(gòu)代碼, 面向組件編程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
package com.lsy.javaweb; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class TokenProcessor { private static final String TOKEN_KEY = "TOKEN_KEY" ; private static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY" ; /** * The singleton instance of this class. */ private static TokenProcessor instance = new TokenProcessor(); /** * The timestamp used most recently to generate a token value. */ private long previous; /** * Protected constructor for TokenProcessor. Use * TokenProcessor.getInstance() to obtain a reference to the processor. */ protected TokenProcessor() { super (); } /** * Retrieves the singleton instance of this class. */ public static TokenProcessor getInstance() { return instance; } /** * <p> * Return <code>true</code> if there is a transaction token stored in the * user's current session, and the value submitted as a request parameter * with this action matches it. Returns <code>false</code> under any of the * following circumstances: * </p> * * <ul> * * <li>No session associated with this request</li> * * <li>No transaction token saved in the session</li> * * <li>No transaction token included as a request parameter</li> * * <li>The included transaction token value does not match the transaction * token in the user's session</li> * * </ul> * * @param request * The servlet request we are processing */ public synchronized boolean isTokenValid(HttpServletRequest request) { return this .isTokenValid(request, false ); } /** * Return <code>true</code> if there is a transaction token stored in the * user's current session, and the value submitted as a request parameter * with this action matches it. Returns <code>false</code> * * <ul> * * <li>No session associated with this request</li> * <li>No transaction token saved in the session</li> * * <li>No transaction token included as a request parameter</li> * * <li>The included transaction token value does not match the transaction * token in the user's session</li> * * </ul> * * @param request * The servlet request we are processing * @param reset * Should we reset the token after checking it? */ public synchronized boolean isTokenValid(HttpServletRequest request, boolean reset) { // Retrieve the current session for this request HttpSession session = request.getSession( false ); if (session == null ) { return false ; } // Retrieve the transaction token from this session, and // reset it if requested String saved = (String) session.getAttribute(TRANSACTION_TOKEN_KEY); if (saved == null ) { return false ; } if (reset) { this .resetToken(request); } // Retrieve the transaction token included in this request String token = request.getParameter(TOKEN_KEY); if (token == null ) { return false ; } return saved.equals(token); } /** * Reset the saved transaction token in the user's session. This indicates * that transactional token checking will not be needed on the next request * that is submitted. * * @param request * The servlet request we are processing */ public synchronized void resetToken(HttpServletRequest request) { HttpSession session = request.getSession( false ); if (session == null ) { return ; } session.removeAttribute(TRANSACTION_TOKEN_KEY); } /** * Save a new transaction token in the user's current session, creating a * new session if necessary. * * @param request * The servlet request we are processing */ public synchronized String saveToken(HttpServletRequest request) { HttpSession session = request.getSession(); String token = generateToken(request); if (token != null ) { session.setAttribute(TRANSACTION_TOKEN_KEY, token); } return token; } /** * Generate a new transaction token, to be used for enforcing a single * request for a particular transaction. * * @param request * The request we are processing */ public synchronized String generateToken(HttpServletRequest request) { HttpSession session = request.getSession(); return generateToken(session.getId()); } /** * Generate a new transaction token, to be used for enforcing a single * request for a particular transaction. * * @param id * a unique Identifier for the session or other context in which * this token is to be used. */ public synchronized String generateToken(String id) { try { long current = System.currentTimeMillis(); if (current == previous) { current++; } previous = current; byte [] now = new Long(current).toString().getBytes(); MessageDigest md = MessageDigest.getInstance( "MD5" ); md.update(id.getBytes()); md.update(now); return toHex(md.digest()); } catch (NoSuchAlgorithmException e) { return null ; } } /** * Convert a byte array to a String of hexadecimal digits and return it. * * @param buffer * The byte array to be converted */ private String toHex( byte [] buffer) { StringBuffer sb = new StringBuffer(buffer.length * 2 ); for ( int i = 0 ; i < buffer.length; i++) { sb.append(Character.forDigit((buffer[i] & 0xf0 ) >> 4 , 16 )); sb.append(Character.forDigit(buffer[i] & 0x0f , 16 )); } return sb.toString(); } } |
以上所述是小編給大家介紹的JavaWeb中HttpSession中表單的重復(fù)提交示例,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://blog.csdn.net/qq_24942951/article/details/58606050