- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章springMVC中基于token防止表单重复提交方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本文介绍了springMVC中基于token防止表单重复提交方法,分享给大家,具体如下:
实现思路:
在springmvc配置文件中加入拦截器的配置,拦截两类请求,一类是到页面的,一类是提交表单的。当转到页面的请求到来时,生成token的名字和token值,一份放到Redis缓存中,一份放传给页面表单的隐藏域。(注:这里之所以使用redis缓存,是因为tomcat服务器是集群部署的,要保证token的存储介质是全局线程安全的,而redis是单线程的) 。
当表单请求提交时,拦截器得到参数中的tokenName和token,然后到缓存中去取token值,如果能匹配上,请求就通过,不能匹配上就不通过。这里的tokenName生成时也是随机的,每次请求都不一样。而从缓存中取token值时,会立即将其删除(删与读是原子的,无线程安全问题).
实现方式:
TokenInterceptor.Java 。
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
|
package
com.xxx.www.common.interceptor;
import
java.io.IOException;
import
java.util.HashMap;
import
java.util.Map;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.apache.log4j.Logger;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import
com.xxx.cache.redis.IRedisCacheClient;
import
com.xxx.common.utility.JsonUtil;
import
com.xxx.www.common.utils.TokenHelper;
/**
*
* @see TokenHelper
*/
public
class
TokenInterceptor
extends
HandlerInterceptorAdapter
{
private
static
Logger log = Logger.getLogger(TokenInterceptor.
class
);
private
static
Map<String , String> viewUrls =
new
HashMap<String , String>();
private
static
Map<String , String> actionUrls =
new
HashMap<String , String>();
private
Object clock =
new
Object();
@Autowired
private
IRedisCacheClient redisCacheClient;
static
{
viewUrls.put(
"/user/regc/brandregnamecard/"
,
"GET"
);
viewUrls.put(
"/user/regc/regnamecard/"
,
"GET"
);
actionUrls.put(
"/user/regc/brandregnamecard/"
,
"POST"
);
actionUrls.put(
"/user/regc/regnamecard/"
,
"POST"
);
}
{
TokenHelper.setRedisCacheClient(redisCacheClient);
}
/**
* 拦截方法,添加or验证token
*/
@Override
public
boolean
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception
{
String url = request.getRequestURI();
String method = request.getMethod();
if
(viewUrls.keySet().contains(url) && ((viewUrls.get(url)) ==
null
|| viewUrls.get(url).equals(method)))
{
TokenHelper.setToken(request);
return
true
;
}
else
if
(actionUrls.keySet().contains(url) && ((actionUrls.get(url)) ==
null
|| actionUrls.get(url).equals(method)))
{
log.debug(
"Intercepting invocation to check for valid transaction token."
);
return
handleToken(request, response, handler);
}
return
true
;
}
protected
boolean
handleToken(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception
{
synchronized
(clock)
{
if
(!TokenHelper.validToken(request))
{
System.out.println(
"未通过验证..."
);
return
handleInvalidToken(request, response, handler);
}
}
System.out.println(
"通过验证..."
);
return
handleValidToken(request, response, handler);
}
/**
* 当出现一个非法令牌时调用
*/
protected
boolean
handleInvalidToken(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception
{
Map<String , Object> data =
new
HashMap<String , Object>();
data.put(
"flag"
,
0
);
data.put(
"msg"
,
"请不要频繁操作!"
);
writeMessageUtf8(response, data);
return
false
;
}
/**
* 当发现一个合法令牌时调用.
*/
protected
boolean
handleValidToken(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception
{
return
true
;
}
private
void
writeMessageUtf8(HttpServletResponse response, Map<String , Object> json)
throws
IOException
{
try
{
response.setCharacterEncoding(
"UTF-8"
);
response.getWriter().print(JsonUtil.toJson(json));
}
finally
{
response.getWriter().close();
}
}
}
|
TokenHelper.java 。
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
package
com.xxx.www.common.utils;
import
java.math.BigInteger;
import
java.util.Map;
import
java.util.Random;
import
javax.servlet.http.HttpServletRequest;
import
org.apache.log4j.Logger;
import
com.xxx.cache.redis.IRedisCacheClient;
/**
* TokenHelper
*
*/
public
class
TokenHelper
{
/**
* 保存token值的默认命名空间
*/
public
static
final
String TOKEN_NAMESPACE =
"xxx.tokens"
;
/**
* 持有token名称的字段名
*/
public
static
final
String TOKEN_NAME_FIELD =
"xxx.token.name"
;
private
static
final
Logger LOG = Logger.getLogger(TokenHelper.
class
);
private
static
final
Random RANDOM =
new
Random();
private
static
IRedisCacheClient redisCacheClient;
// 缓存调用,代替session,支持分布式
public
static
void
setRedisCacheClient(IRedisCacheClient redisCacheClient)
{
TokenHelper.redisCacheClient = redisCacheClient;
}
/**
* 使用随机字串作为token名字保存token
*
* @param request
* @return token
*/
public
static
String setToken(HttpServletRequest request)
{
return
setToken(request, generateGUID());
}
/**
* 使用给定的字串作为token名字保存token
*
* @param request
* @param tokenName
* @return token
*/
private
static
String setToken(HttpServletRequest request, String tokenName)
{
String token = generateGUID();
setCacheToken(request, tokenName, token);
return
token;
}
/**
* 保存一个给定名字和值的token
*
* @param request
* @param tokenName
* @param token
*/
private
static
void
setCacheToken(HttpServletRequest request, String tokenName, String token)
{
try
{
String tokenName0 = buildTokenCacheAttributeName(tokenName);
redisCacheClient.listLpush(tokenName0, token);
request.setAttribute(TOKEN_NAME_FIELD, tokenName);
request.setAttribute(tokenName, token);
}
catch
(IllegalStateException e)
{
String msg =
"Error creating HttpSession due response is commited to client. You can use the CreateSessionInterceptor or create the HttpSession from your action before the result is rendered to the client: "
+ e.getMessage();
LOG.error(msg, e);
throw
new
IllegalArgumentException(msg);
}
}
/**
* 构建一个基于token名字的带有命名空间为前缀的token名字
*
* @param tokenName
* @return the name space prefixed session token name
*/
public
static
String buildTokenCacheAttributeName(String tokenName)
{
return
TOKEN_NAMESPACE +
"."
+ tokenName;
}
/**
* 从请求域中获取给定token名字的token值
*
* @param tokenName
* @return the token String or null, if the token could not be found
*/
public
static
String getToken(HttpServletRequest request, String tokenName)
{
if
(tokenName ==
null
)
{
return
null
;
}
Map params = request.getParameterMap();
String[] tokens = (String[]) (String[]) params.get(tokenName);
String token;
if
((tokens ==
null
) || (tokens.length <
1
))
{
LOG.warn(
"Could not find token mapped to token name "
+ tokenName);
return
null
;
}
token = tokens[
0
];
return
token;
}
/**
* 从请求参数中获取token名字
*
* @return the token name found in the params, or null if it could not be found
*/
public
static
String getTokenName(HttpServletRequest request)
{
Map params = request.getParameterMap();
if
(!params.containsKey(TOKEN_NAME_FIELD))
{
LOG.warn(
"Could not find token name in params."
);
return
null
;
}
String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD);
String tokenName;
if
((tokenNames ==
null
) || (tokenNames.length <
1
))
{
LOG.warn(
"Got a null or empty token name."
);
return
null
;
}
tokenName = tokenNames[
0
];
return
tokenName;
}
/**
* 验证当前请求参数中的token是否合法,如果合法的token出现就会删除它,它不会再次成功合法的token
*
* @return 验证结果
*/
public
static
boolean
validToken(HttpServletRequest request)
{
String tokenName = getTokenName(request);
if
(tokenName ==
null
)
{
LOG.debug(
"no token name found -> Invalid token "
);
return
false
;
}
String token = getToken(request, tokenName);
if
(token ==
null
)
{
if
(LOG.isDebugEnabled())
{
LOG.debug(
"no token found for token name "
+ tokenName +
" -> Invalid token "
);
}
return
false
;
}
String tokenCacheName = buildTokenCacheAttributeName(tokenName);
String cacheToken = redisCacheClient.listLpop(tokenCacheName);
if
(!token.equals(cacheToken))
{
LOG.warn(
"xxx.internal.invalid.token Form token "
+ token +
" does not match the session token "
+ cacheToken +
"."
);
return
false
;
}
// remove the token so it won't be used again
return
true
;
}
public
static
String generateGUID()
{
return
new
BigInteger(
165
,RANDOM).toString(
36
).toUpperCase();
}
}
|
spring-mvc.xml 。
1
2
3
4
5
6
7
8
9
|
<!-- token拦截器-->
<
bean
id
=
"tokenInterceptor"
class
=
"com.xxx.www.common.interceptor.TokenInterceptor"
></
bean
>
<
bean
class
=
"org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"
>
<
property
name
=
"interceptors"
>
<
list
>
<
ref
bean
=
"tokenInterceptor"
/>
</
list
>
</
property
>
</
bean
>
|
input.jsp 在form中加如下内容:
1
2
3
|
<input type=
"hidden"
name=
"<%=request.getAttribute("
xxx.token.name
") %>"
value=
"<%=token %>"
/>
<input type=
"hidden"
name=
"xxx.token.name"
value=
"<%=request.getAttribute("
xxx.token.name
") %>"
/>
|
当前这里也可以用类似于struts2的自定义标签来做.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:http://blog.csdn.net/letter_believe/article/details/76034791 。
最后此篇关于springMVC中基于token防止表单重复提交方法的文章就讲到这里了,如果你想了解更多关于springMVC中基于token防止表单重复提交方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想知道如何考虑需要您做出某些选择才能看到最终结果的搜索系统。我说的是 select 表单,您可以在其中根据您的选择继续操作,然后您会看到结果。 下面描述了我正在谈论的一个随机示例。想象一下 Init
您好,我目前正在编写一些软件来管理我们的库存。我搜索了 2 个表 master_stock(保存每一个股票代码和描述)库存(保存库存代码、地点、数量...) 一切都很好,但这是我遇到的问题。 假设我的
我有 2 个表,我想合并其数据。id 是我的关键字段(增量且不同)。表1和表2字段说明例如:id - 名称 - 值 我想将表2的所有数据插入表1,它们有不同的数据,但在某些行中有相同的id。 所以当我
我正在努力解决汇编中的一个问题,我必须获取十六进制代码的第一个字节 (FF) 并将其复制到整个值中: 0x045893FF input 0xFFFFFFFF output 我所做的
我有 Eclipse Indigo 版本,我可以在其中运行 Java 和 C++ 项目。 但我只想使用另一个 Eclipse 来编写 C++ 项目。所以我将 eclipse(不是工作区)的源文件夹复制
This question already has answers here: What is a NullPointerException, and how do I fix it? (12个答案)
This question already has answers here: Numbering rows within groups in a data frame (8个答案) 5个月前关闭。
我知道用q记录到寄存器中,但我想知道是否可以设置一些东西来快速调用最后一个记录,就像一样。 回顾最后一个简短的编辑命令(有关 的讨论请参阅 here。)。 我知道@@,但它似乎只有在执行@z之后才起作
来自 Eclipse 并且一直习惯于复制行,发现 Xcode 没有这样的功能是很奇怪的。或者是吗? 我知道可以更改系统范围的键绑定(bind),但这不是我想要的。 最佳答案 要删除一行:Ctrl-A
假设我有一个包含元素的列表,例如[1,2,3,4,5,6,7,8]。我想创建长度为 N 的该元素的所有排列。 因此,对于N = 4,它将是[[1,1,1,1],[1,1,1,2],[1,1,2,1],
我有一个带有 JMenu 的 JFrame。当我在某些情况下添加包含图像的 JPanel 时,程序首次启动时菜单会重复。调整大小时重复的菜单消失。任何建议都非常感激。谢谢。代码如下: public c
我正在尝试查找目录中文件的重复项。 我对这个 block 有一个问题,它以文件地址作为参数: public void findFiles(ArrayList list){ HashMap hm
我知道这个问题已经发布并且已经给出了答案,但我的情况不同,因为我在单个方法上填充多个下拉列表,所以如果我点击此链接 After every postback dropdownlist items re
我正在尝试为我的日历应用程序实现重复模式。我希望它的工作方式与 Outlook 在您设置重复约会时的工作方式相同。 public async Task> ApplyReccurrencePeriod
我有一个利用 cookie 来支持准向导的应用程序(即,它是一组相互导航的页面,它们必须以特定顺序出现以进行注册)。 加载 Logon.aspx 页面时 - 默认页面 - 浏览器 cookie 看起来
我有 3 个输入,代码检查它们是否为空,如果为空,则将变量值添加到输入中。 所以我有 3 个具有值的变量: var input1text = "something here"; var input2t
根据数组的长度更改数组的每个元素的最佳方法是什么? 例如: User #1 input = "XYZVC" Expected Output = "BLABL" User #2 input = "XYZ
我在让 Algolia 正常工作时遇到了一些麻烦。我正在使用 NodeJS 并尝试在我的数据库和 Algolia 之间进行一些同步,但由于某种原因似乎随机弹出大量重复项。 如您所见,在某些情况下,会弹
遵循以下规则: expr: '(' expr ')' #exprExpr | expr ( AND expr )+ #exprAnd | expr ( OR expr )+ #exprO
我有一个布局,我想从左边进入并停留几秒钟,然后我希望它从右边离开。为此,我编写了以下代码: 这里我在布局中设置数据: private void loadDoctor(int doctorsInTheL
我是一名优秀的程序员,十分优秀!