Spring 控制器方法不需要的逗号分隔参数
我发现 Spring MVC 控制器存在一个奇怪的问题。该方法的目的是设置密码。它采用两个表单参数“password”和“confirmPassword”。第一次调用表单时,这工作得很好——字段被传递给方法。
当第二次提交表单时,就会出现问题。如果第一次填写表单错误,用户会正确地返回到表单页面并提示重新输入密码。然而,该方法的参数在第二次尝试时不正确。参数是一个逗号分隔的列表,其中包括第一个表单条目与第二个表单条目连接。
示例:
第一个包含字段“password”的表单帖子的值为“abc”。方法参数“password”的值为“abc”。
第二个表单帖子包含字段“password”和值“xyz”。方法参数“password”的值为“xyz,abc”。
Spring MVC 文档并没有表明有多大用处。不知何故,旧的表单帖子被记住并包含在内。有人有解决这个问题的经验吗?
控制器方法如下:
@RequestMapping(value = "/account/reset", method = RequestMethod.POST)
public String resetPassword(@RequestParam("password") String password,
@RequestParam("confirmPassword") String confirmPassword,
@RequestParam("hash") String hash, ModelMap model) throws EncryptionException
{
String userName = stringEncrypterService.decrypt(hash);
User user = userService.findUserByPath(userName);
if (!password.equals(confirmPassword))
{
model.put("hash", hash);
model.put("user", user);
model.put("error",
"The two passwords you entered below do not match. Please try again.");
return "users/resetPassword";
}
userService.updatePassword(user, password);
emailService.sendUserInfoChange(user);
return "redirect:/session/signin?passwordReset=true";
}
更新。一些回复者表示,可能有问题的帖子有额外的 URL 参数或隐藏的表单字段,导致字段名称重复。我向 Fiddler 确认情况并非如此。这是第三次尝试的原始请求。 (稍微编辑以删除会话 cookie)。
POST http://wintest.foriodev.com/simulate/account/reset/ HTTP/1.1
Host: wintest.foriodev.com
Connection: keep-alive
Referer: http://wintest.foriodev.com/simulate/account/reset/
Content-Length: 73
Cache-Control: max-age=0
Origin: http://wintest.foriodev.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16
Content-Type: application/x-www-form-urlencoded
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: AUTOLOGIN_TOKEN=xyz; SIMULATE_WORKER=wintest; JSESSIONID=xyz;
password=a&hash=xyz&confirmPassword=a&save=Reset+Password
I'm seeing a strange problem with a Spring MVC controller. This method is intended to set the password. It takes two form parameters "password" and "confirmPassword". The first time the form is called, this works fine-- the fields are passed to the method.
The problem occurs when the form is submitted a second time. If the form is incorrectly filled out the first time, the user is correctly sent back to the form page and prompted to enter the password again. However the arguments to the method are incorrect on the second try. The arguments are a comma separated list which includes the first form entry concatenated with the second.
Example:
First form post with field "password" has a value of "abc". Method argument "password" has value "abc".
Second form post with field "password" and a value of "xyz". Method argument "password" has value "xyz,abc".
The Spring MVC docs don't indicate much useful. Somehow the old form post is remembered and included. Anyone have experience in solving this?
Controller method is below:
@RequestMapping(value = "/account/reset", method = RequestMethod.POST)
public String resetPassword(@RequestParam("password") String password,
@RequestParam("confirmPassword") String confirmPassword,
@RequestParam("hash") String hash, ModelMap model) throws EncryptionException
{
String userName = stringEncrypterService.decrypt(hash);
User user = userService.findUserByPath(userName);
if (!password.equals(confirmPassword))
{
model.put("hash", hash);
model.put("user", user);
model.put("error",
"The two passwords you entered below do not match. Please try again.");
return "users/resetPassword";
}
userService.updatePassword(user, password);
emailService.sendUserInfoChange(user);
return "redirect:/session/signin?passwordReset=true";
}
Update. Several responders have suggested that perhaps the problematic posts have extra URL parameters or hidden form fields resulting in duplicate field names. I confirmed with Fiddler that this is not the case. Here's the raw request from the third try. (slightly edited to remove session cookie).
POST http://wintest.foriodev.com/simulate/account/reset/ HTTP/1.1
Host: wintest.foriodev.com
Connection: keep-alive
Referer: http://wintest.foriodev.com/simulate/account/reset/
Content-Length: 73
Cache-Control: max-age=0
Origin: http://wintest.foriodev.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16
Content-Type: application/x-www-form-urlencoded
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: AUTOLOGIN_TOKEN=xyz; SIMULATE_WORKER=wintest; JSESSIONID=xyz;
password=a&hash=xyz&confirmPassword=a&save=Reset+Password
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我认为这是因为
return "redirect:/session/signin?passwordReset=true";
使用重定向:框架使用 url 重写技术,类似于 servlet 中的基本 response.sendRedirect(...),因此参数、值会与请求一起附加到下一个后续请求,依此类推。
尝试使用不同的机制而不是“重定向:”
I think the reason for this is because of
return "redirect:/session/signin?passwordReset=true";
With redirect: the framework uses the url-rewriting technique, similar to the basic response.sendRedirect(...) in servlets, and hence the parameter,values are appended along with the request to the next consequent requests and so on.
Try using a different mechanism rather than "redirect:"
所以经过一年多的时间,我明白了这一点。
问题在于自定义拦截器存储每个请求的请求缓存。我这样做是为了当用户登录时,他会返回到上一个屏幕。但这对于“重置密码”屏幕来说是完全不合适的。
该机制是,当调用 request.getParameter(Name) 时,SavedRequestCacheWrapper 会将实际的 HTTP 请求参数与上次请求中存储的参数连接起来。
解决方案是 (a) 让该拦截器忽略重置密码屏幕,以及 (b) 忽略所有 post 请求以防止这种请求参数值串联。
对于其他回复者,感谢所有好主意。 (感谢那位为此悬赏的匿名用户——感谢你让我回去再看一眼。)
So after more than a year I figured this out.
The problem was a custom interceptor that stored the request cache on each request. I do this so that when a user logs on, he will go back to the previous screen. But it's completely inappropriate for a "reset password" screen.
The mechanism was that when request.getParameter(Name) is called the SavedRequestCacheWrapper would then concatenate the actual HTTP request parameters with the stored parameters from that last request.
The solution is to (a) have this interceptor ignore the reset password screen and (b) ignore all post requests to prevent this kind of request parameter value concatenation.
To the other responders, thanks for all the good ideas. (And to the anonymous user who put a bounty on this - thanks for causing me to go back and take another look.)
如果您第三次发布,列表也会增加到三个吗?这表明问题与用户的会话有关。或者,如果列表保持在两个,则问题出在请求中。我猜测该列表会增长到三个,因为您从 Fiddler 发布的信息没有显示请求中有重复的迹象。
要确认数据在会话中,请检查其内容(例如,打印到日志、使用调试器、打印到开发环境中的网页)以查找相同键下的数据——“密码”、“哈希”、等。
另请参阅 此关于为什么数据可能在用户会话中的其他响应,例如。您在某处使用了 @SessionAttributes 注释。
If you post a third time, does the list grow to three, too? This would indicate that the problem is related to the user's session. Or, if the list stays at two, then the problem is in the request. I'm guessing that the list grows to three, since the info you posted from Fiddler shows no sign of duplication in the request.
To confirm the data is in the session, check its contents (ex. print to a log, use a debugger, print to the web page in a dev environment) for data under the same keys -- "password", "hash", etc.
Also, see this other response about why data might be in the user's session, ex. you used the @SessionAttributes annotation somewhere.
当您有多个具有相同名称的表单字段时,您将获得逗号分隔的值。造成这种情况的一个常见原因是隐藏输入和文本输入具有相同的名称。页面第一次发布时,隐藏的输入将为空,因此没有逗号。第二次(及后续)页面发布时,隐藏的输入将具有值,因此您会得到逗号。
You will get comma seperated values when you have multiple form fields that have the same name. A common cause for this is to have hidden inputs and text inputs with the same name. The first time the page posts, the hidden inputs will be empty so, no commas. The second (and subsiquent) time the page posts, the hidden inputs will have values, so you get commas.
看起来旧值以某种方式显示为 GET 参数,即您可以在第二种形式中使用
It looks like the old values somehow appear as GET parameters, i.e. either you have
<form action = ".../account/reset?password=abc">
in the second form, oraction
is empty and URL of the second form itself is.../account/reset?password=abc
. Though I can't find anything responsible for it in your code.当您从验证步骤返回时,您是否以某种方式再次指定表单字段?使用 Firebug 或类似的东西来检查您发送的内容和/或发布您的 jsp(或类似)页面。
Are you somehow specify the form fields a second time when you come back from the validation step? Use Firebug or something similar to inspect what you're sending and/or post your jsp (or similar) page.
看起来这可能是由问题 https://jira.springsource.org/browse/SEC- 引起的1258
“SavedRequestAwareWrapper 在 3.0 M2 中与 Spring MVC 结合使用会导致问题”
Looks like this might be caused by the issue https://jira.springsource.org/browse/SEC-1258
"SavedRequestAwareWrapper causes trouble in 3.0 M2 in combination with Spring MVC"
就我而言,我将隐藏字段与未使用的路径相关联。删除隐藏字段帮助我解决了这个问题。
In my case, I had associated hidden field with a path that was unused. Removing the hidden field helped me resolve the issue.