使用Spring MVC 3 + Hibernate更新持久对象的“正确”或“安全”方法是什么?
问题内容:
给定一个非常简单的对象:
class User {
private Integer id;
private String name;
public User() {}
public Integer getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
和一个非常简单的控制器动作:
@RequestMapping(value="/edit/{id}/**", method=RequestMethod.POST)
public String editFromForm(@PathVariable("id") Integer id, @Valid User user, BindingResult bindingResult, Model model) {
// If we have errors, don't save
if(bindingResult.hasErrors()) {
// Put what they did in the model and send it back
model.addAttribute(user);
return "users/edit";
} else {
userDAO.save(user);
}
// Show them the updated page on success
return "redirect:/users/" + user.getId() + "/" + user.getName();
}
和一个非常简单的形式:
<sf:form method="POST" modelAttribute="user">
<label for="user_name">Name:</label>
<sf:input path="name" id="user_name" />
<input type="submit" value="save" /><sf:errors path="name" cssClass="error" />
</sf:form>
我 应该 如何更新数据库中的实体?当前(由于saveOrUpdate()
是我DAO
save()
方法背后的实际hibernate调用,因此将保留一个新对象,而不是更新现有对象,因为id
未在通过表单提交创建的对象上设置字段。
我想出了两种可能的解决方案,但我不确定哪种方法在保持事物清洁和安全方面是最好的(这样恶意用户就不能仅对他们想要的对象ID进行编辑)。
- 将URL参数中的id插入到来自模型绑定程序的对象中
id
在表单中有一个隐藏字段,并让模型绑定器附加ID
在这两种情况下,都没有进行检查以确保对象仍然相同,例如某种形式的校验和。其他人如何处理?有没有明确的例子可以解决这个问题?
出现的另一个问题是,我宁愿不需要一种setId()
方法,因为Hibernate正在管理所有id。根据我的判断,Spring
MVC模型绑定程序仅在具有预期的getter和setter的情况下才可以绑定字段。是否有其他方法可以应用新状态,例如通过URL的id
参数从数据库获取当前用户,然后将新状态应用到该状态,而不必显式地编码所有字段副本?
我敢肯定有一个相当简单直接的方法来解决这个问题,但是我那热血沸腾的大脑似乎无法提出合适的解决方案。
我是Spring + Hibernate的新手,所以请原谅我这是那些平淡无奇的话题之一,但是我找不到能说明我非常简单情况的清晰示例。如果 已
被充分覆盖在别处,请点我在正确的方向。
问题答案:
我想出了两种可能的解决方案,但我不确定哪种方法在保持事物清洁和安全方面是最好的(这样恶意用户就不能仅对他们想要的对象ID进行编辑)。
您提到的两种方法都无法真正处理试图编辑未经授权的对象的用户。最终,提交表单的用户需要告诉您他们要提交数据的对象-
无论是在URL参数中还是在隐藏的表单参数中。我要说的是,您选择的两个是样式和个人喜好。
但是,无论选择什么,您需要做的是在处理表单提交时验证当前登录的用户是否有权更改对象。这意味着您需要使用该应用程序包含的“允许执行此操作”的任何逻辑来检查该用户是否有权编辑当前对象ID。