所以最终必须比较当前请求的user-id和表示所请求的日历所有者的user-id.但我想知道在哪里做这个我的想法:
>我可以在DAO层面上做.但是,每个DAO方法都需要一个额外的参数来表示用户id,这些参数使得这些方法更加冗长并减少了可重用性.
例如.
def findCalById(id:Int):Future [Option [Calendar]]
变
def findCalById(id:Int,ownerId:Int):Future [Option [Calendar]]
一个优点是,权限检查基本上在查询级别完成,这意味着如果用户无法访问日历,则不会从数据库返回日历.但是再一次:如果在某些情况下没有返回日历,那么您如何区分不存在的日历和当前用户无法访问的现有日历?两种不同的场景,产生相同的结果.
>另一种选择可能是将DAO保留在此之外,并在服务层或类似的东西中进行检查.这意味着在DAO返回请求的日历之后执行该检查.这种方法听起来比另一种更灵活,但也意味着在用户无法访问所请求的日历的情况下,所请求的日历仍然消耗带宽和内存,因为在任一种情况下都从数据库中获取带宽和内存.
也许有其他选项我甚至没有考虑.有没有最佳做法?
Btw:我的Web应用程序没有日历,这只是一个例子来说明问题.
解决方法
您可以将用户ID设置为隐式参数,使这些方法对上游用户更友好.你也可以让它返回一个Try(或者也许是一个)来解决你对区分缺失和不可访问的对象情况的关注:
case class UserId(id: Int) def findCalById(id: Int)(implicit user: UserId): Future[Try[Option[Calendar]]] = ???
然后,呼叫者可以这样做:
implicit val currentUser = UserId(request.getUserId) dao.findCalById(request.getCalendarId).map { case Failure(IllegalAccessException()) => "You are not allowed to use this calendar" case Return(None) => "Calendar not found" case Return(Some(cal)) => calendarToString(cal) }
另一方面,如果可能的情况下DAO可能在没有用户上下文的情况下被使用(也许是“管理员”应用程序),则可以考虑将其子类化以向“常规应用程序”提供访问控制,也许,只需做一个额外的角色,允许用户访问有关所有权的所有日历,然后在您的管理应用程序中使用该“超级用户”.
我不用担心在检查访问之前必须加载对象的成本(即使对象加载真的很贵,它应该是一个罕见的事情,有人试图访问他不拥有的对象).我认为,反对服务层方法的更强有力的论据是代码的可重用性和模块化:具有公共接口的DAO类的存在表明它至少可以被多个组件重用.看来愚蠢的是要求所有这样的组件实现自己的访问检查,特别是考虑到这样的合同是不可执行的:没有办法确保在几年后决定使用你的DAO类的人,会记住这个要求(或者关心阅读评论).如果您正在生成访问数据库的图层,那么您也可以使其对某些东西有用.