保护spring mvc和hibernate的最佳实践
Posted
技术标签:
【中文标题】保护spring mvc和hibernate的最佳实践【英文标题】:Best practice to secure spring mvc and hibernate 【发布时间】:2016-10-20 05:36:56 【问题描述】:假设我有一个 table z 包含 a,b,c 列。
a 列和 b 列可供任何授权用户使用,c 列只能由管理员使用。
我的休眠实体基于表 z。我的 spring mvc 控制器有一个读写方法。我可以使用 spring security 使用角色来保护 mvc 控制器。
问题是...我的客户端(通过 rest/json 与控制器进行角度交谈)可以通过设置 c 的值(即使客户端前端没有明确提供)来访问所有列,只需设置一个 json 对象并将其发送到控制器的 write 方法。同样调用 read 方法会将 c 列的值返回给任何用户。
根据角色访问该表的最佳做法是什么?
好的,下面是一个具体的例子:
实体:
@Entity
@Table(name = "hotel")
@XmlRootElement
public class Hotel extends BaseEntity implements Serializable
private String name;
private String street;
private String houseNo;
private String postalcode;
private String city;
private String country;
private String shortDesc;
private Boolean landing; // admin acceess only
后端外观:
@Component
public class HotelAccessImpl extends BackendBaseAccess implements HotelAccess
@Autowired
private AccountAccess accountAccess;
@Override
public List<Hotel> findAll()
TypedQuery<Hotel> query = getEm().createQuery("FROM Hotel WHERE DELETED = false ORDER BY CREATED DESC",
Hotel.class);
List<Hotel> results = query.getResultList();
return results;
@Override
@Transactional(value = "transactionManager", propagation = Propagation.REQUIRES_NEW)
public Hotel upsert(Hotel hotel)
if (find(hotel.getId()) == null)
getEm().persist(hotel);
else
hotel = getEm().merge(hotel);
getEm().flush();
return hotel;
API(控制器,我在方法级别“仅”限制访问)
@RestController
@RequestMapping("hotel")
public class HotelController extends BaseController
private static Logger logger = LogManager.getLogger(HotelController.class);
@Autowired
private FileAccess fileAccess;
@Autowired
private HotelAccess hotelAccess;
@Autowired
private MailAccess mailAccess;
@RequestMapping(value = "list", method = RequestMethod.GET)
public ResponseEntity<List<Hotel>> findAll()
logger.info("FindAll");
return new ResponseEntity<List<Hotel>>(hotelAccess.findAll(), HttpStatus.OK);
@RequestMapping(value = "upsert", method = RequestMethod.POST)
public ResponseEntity<Hotel> upsert(@RequestBody Hotel hotel)
logger.info("Upsert: " + hotel.getName());
if (isAuthorized(hotel.getAccount()))
Hotel response = hotelAccess.upsert(hotel);
if (response.isInitial())
mailAccess.sendHotelUpsert(response);
return new ResponseEntity<Hotel>(response, HttpStatus.OK);
else
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
@Secured( "ROLE_ADMIN" )
@RequestMapping(value = "delete", method = RequestMethod.POST)
public ResponseEntity<Hotel> delete(@RequestBody Hotel hotel)
logger.info("Delete: " + hotel.getId());
boolean deleted = hotelAccess.delete(hotel);
return (deleted) ? new ResponseEntity<>(HttpStatus.OK) : new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
【问题讨论】:
需要将其缩小到更具体的问题。见How to Ask 添加了一些代码,希望现在能给出更多解释。 【参考方案1】:您需要考虑保护实体数据的编组和解组。
换句话说,对于非管理员用户,您只想将字段 A
和 B
编组到客户端,并从客户端解组字段 A
和 B
。对于管理员用户,您需要编组和取消编组所有字段。
当用户上下文不是管理员时,您需要防止向基于 javascript 的客户端泄露有关字段 C
的安全信息。根据字段C
值的安全性质,阻止非管理员用户插入/更新字段C
是不够的。
【讨论】:
有没有办法在运行时进行基于角色的编组?截至目前,编组是由杰克逊根据控制器方法持有的实体完成的——在我的例子中是实体的所有成员(包括管理员成员)。 ...当成员 c 没有解组到实体(由于安全规则)并且实体被写入数据库时会发生什么? hibernate 会跳过更新列还是会用空值覆盖列?【参考方案2】:应该不会太难:
使用相同或不同的控制器,为读取/写入创建单独的 API,一组用于普通用户,另一组用于管理员用户。
使用 spring security 仅允许管理员访问 Admin API。
对于处理普通用户读取的 API,在发送值之前,将您不希望普通用户看到的 Z 的任何元素清空:
@RequestMapping(value = "/my/foo/id", method = RequestMethod.GET)
public Foo getFoo(@PathVariable String id, HttpServletResponse response)
Foo foo = fooDao.getFoo(id);
if (foo != null)
foo.setC(null);
return foo;
else
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return null;
对于写入,根据传入对象的内部 ID 从 Hibernate 检索元素,并仅使用传入对象中允许用户更新的字段对其进行更新,然后将该对象持久保存到数据库中。
【讨论】:
这种方式对我来说似乎是多余的。我也考虑过这样做,但我不确定这是否是好的做法。Naros 的建议似乎是一种更好的方法。 如果需要,可以将证明冗余的两个实现部分分解为一个公共私有(非外部可用)方法。但对我来说,基于 API URL 的安全性(如第 39-40 行 here)似乎更安全,因此除非满足角色,否则甚至无法输入方法,而不是创建一个所有用户都可以的通用方法输入并依靠方法的实现来确保普通用户不会做只有管理员才能做的事情。以上是关于保护spring mvc和hibernate的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
spring mvc+spring + hibernate 整合
spring mvc+spring + hibernate 整合
请解释spring MVC,hibernate和Spring security