architecture - RESTful 授权

标签 architecture rest permissions authorization

我正在为真实世界组织的成员在 Rails 中构建一个基于社区的站点。我正在努力遵循 RESTful 设计的最佳实践,其中大部分或多或少是书本上的。使我的大脑在整洁的 RESTful 圈子中运转的问题是授权问题。对于广泛接受的 RESTful 解决方案,身份验证是一个容易解决的长期问题,但 RESTful 授权似乎有点黑艺术。我试图找到一种方法,该方法将提供最通用和最灵活的框架来控制对资源的访问,同时尽可能简单,同时符合 RESTful 架构。 (还有一匹小马。)

注意事项:

  • 我需要控制对各种资源的访问,例如用户、页面、帖子等。
  • 对给定资源的授权必须比简单的 CRUD 更细粒度。
  • 我希望允许自己和其他人在应用程序中编辑授权规则。
  • 应该允许授权规则依赖于谓词,例如(概念上)Owner(User, Resource) 或 Locked(Topic)

  • 考虑(2)是我最关心的问题。我的权限概念和 RESTful 操作概念之间似乎存在阻抗不匹配。例如,采用 Posts(如在留言板中)。 REST 规定对 Post 资源存在四种操作:创建、读取、更新和删除。说一个用户应该能够更新他自己的帖子很简单,但只有某些用户(或角色或组)应该被允许锁定它们。表示锁定的传统方式是在 Post 的状态内,但这会导致用户在相同条件下可能无法更新 Post 的气味,这取决于他提供的(完全有效的)值。我似乎很清楚,确实有两种不同的 Action 可以改变 Post 的状态,而硬塞它们只是为了掩饰对 RESTful 原则的违反。

    (我应该注意到,这个问题与由于无效或不一致数据导致更新失败的问题截然不同——来自非特权用户的锁定请求原则上是非常有效的,只是被禁止。)

    Isn't decomposition another word for rot?



    这可以通过分解帖子来克服:锁是特定帖子的子资源,然后创建或销毁一个人可能拥有单独的权限。该解决方案具有 REST 的特点,但同时也带来了理论和实践上的困难。如果我排除了锁,那么其他属性呢?假设我决定,在反复无常的情况下,只允许管理员成员修改帖子的标题?一个简单的授权更改将需要对数据库进行重组以适应它!这不是一个很好的解决方案。为了在分解策略下实现这种灵活性,需要每个属性都是一种资源。这有点进退两难。我隐含的假设是资源在数据库中表示为表。在这种假设下,每个属性的资源意味着每个属性的表。显然,这是不切实际的。然而,消除这个假设会导致表和资源之间的阻抗不匹配,这可能会打开它自己的蠕虫 jar 。使用这种方法需要比我给出的更深入的考虑。一方面,用户合理地期望能够一次编辑多个属性。请求去哪里了?包含所有属性的最小资源?并行到每个单独的资源?到月球?

    Some of these things are not like the others…



    假设我不分解属性。替代方案似乎是为每个资源定义一组权限。然而,在这一点上,REST 的同质性丢失了。为了定义资源的访问规则,系统必须具有该资源能力的特定知识。此外,现在不可能将权限一般地传播到后代资源——即使子资源具有同名权限,这些权限之间也没有内在的语义联系。在我看来,定义一组类似于 REST 的标准权限似乎是两全其美的做法,因此我不得不为每种类型的资源设置单独的权限层次结构。

    Well, we did do the nose. And the hat. But it's a resource!



    我看到的一个建议可以减轻上述方法的一些缺点,就是将权限定义为对资源的创建/删除和对属性的读/写。该系统是属性作为资源和每个资源的权限之间的折衷:仍然只剩下 CRUD,但出于授权的目的,读取和更新属于属性,可以将其视为伪资源。这提供了属性作为资源方法的许多实际好处,尽管概念完整性在一定程度上受到了损害。权限仍然可以从资源传播到资源,从资源传播到伪资源,但永远不会从伪资源传播。我还没有完全探索这种策略的后果,但它似乎很有希望。我突然想到,这样的系统最适合作为模型的一个组成部分。例如,在 Rails 中,它可能是对 ActiveRecord 的改造。 .这对我来说似乎相当激烈,但授权是一个基本的跨领域问题,这可能是合理的。

    Oh, and don't forget about the pony



    所有这些都忽略了预测权限的问题。显然,用户应该能够编辑自己的帖子,但不能编辑其他人的帖子。同样明显的是,管理员编写的权限表不应该为每个用户单独记录。这并不是一个不常见的要求——诀窍是让它通用。我认为我需要的所有功能都可以通过仅将规则设为谓词来获得,以便可以快速且立即地确定规则的适用性。规则“allow User write Post where Author(User, Post)”将转换为“for all User, Post such that Author(User, Post), allow User write Post”,将“deny all write Post where Locked(Post)”转换为“for all Post such that Locked(Post), deny all write Post”。 (如果所有这些谓词都可以用一个用户和一个资源来表达,那就太好了。)概念上产生的“最终”规则将是非谓词的。这就提出了如何实现这样一个系统的问题。谓词应该是 Model 类的成员,但我不确定如何在规则的上下文中优雅地引用它们。安全地这样做需要某种反射(reflection)。在这里,我再次感觉到这需要对模型实现进行改造。

    How do you spell that again?



    最后一个问题是如何将这些授权规则最好地表示为数据。一个数据库表可能会起作用,枚举列用于允许/拒绝和 C/R/U/D(或者可能是 CRUD 位?或者可能是 {C, R, U, D} × {allow, deny, inherit}?) ,以及一个带有路径的资源列。也许,为了方便起见,“继承”一点。就谓词而言,我不知所措。单独的表?当然有很多缓存可以防止它太慢。

    我想这是很多要求。在问这个问题之前我试着做我的功课,但在这一点上我真的需要一个外部的视角。我很感激你们中的任何人可能对这个问题提出的任何意见。

    最佳答案

    抱歉,我没有时间公正地解决这个问题,但是很高兴看到一些关于 SO 的经过深思熟虑的问题。
    以下是一些评论:

    不要陷入将 HTTP 动词映射到 CRUD 的陷阱。是的 GET 和 DELETE 映射非常干净,但 PUT 可以执行创建和更新(但只能完全替换)并且 POST 是通配符动词。 POST 实际上是处理所有不适合 GET、PUT 和
    删除。

    使用属性来表示对象的状态只是状态管理的一种方法。我猜你可以想象以下请求可能会做什么:

    POST /LockedPosts?url=/Post/2010
    

    子资源也是管理资源当前状态的有效方法。我不会觉得有必要以一致的方式处理资源的“状态”属性及其“数据”属性。

    尝试将资源直接映射到表将严重限制您。不要忘记,当您遵循 REST 约束时,您可以使用的动词突然变得非常有限。您需要能够在您使用的资源中发挥创造力来弥补这一点。将自己限制为一种资源等于一张表将严重限制最终应用程序的功能。

    我们经常看到 Rails、ASP.NET MVC 和 WCF Rest 用户在 StackOverflow 上发布关于如何在 REST 的约束下做某些事情的问题。问题通常不是 REST 的约束,而是框架在支持 RESTful 应用程序方面的局限性。我认为首先找到一个问题的 RESTful 解决方案,然后看看它是否可以映射回您选择的框架是必不可少的。

    至于创建一个比资源本身更精细的权限模型。请记住,关键的 REST 约束之一是超媒体。超媒体不仅可以用于查找相关实体,还可以用于表示有效/允许的状态转换。如果您返回一个包含嵌入链接的表示,有条件地基于权限,那么您可以控制谁可以执行哪些操作。即,如果用户有权解锁 POST 342,那么您可以返回以下嵌入在表示中的链接:
    <Link href="/UnlockedPosts?url=/Post/342" method="POST"/>
    

    如果他们没有该权限,则不要返回链接。

    我认为您在这里遇到的困难之一是您试图一次解决太大的问题。我认为您需要将尝试向客户端公开的 RESTful 接口(interface)视为一个与您将如何管理权限和谓词以管理域模型中的授权不同的问题。

    我意识到我没有直接回答你的任何问题,但希望我提供了一些可能在某种程度上有所帮助的观点。

    关于architecture - RESTful 授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1408571/

    相关文章:

    linux - 如何创建我自己的 "sudo"程序?

    python - 将功能分解为被动(算法)和主动(执行)对象

    rest - Keycloak 服务到服务

    java - Spring Boot 应用程序延迟将请求传递到 Controller 几分钟

    spring - 如何使用 spring 3.0 创建 restful web 服务?

    c# - 如何添加对 AWS SQS 队列的权限?

    post - 尝试在 Box 中创建指向文件夹的共享链接时出现访问被拒绝错误消息

    Python 3 - 模块和包困惑 - 架构

    java - 在节点之间传递数据。如何保持它们清洁?

    web-applications - 了解 Web 应用程序中的层