在域对象中执行属性级授权

Son*_*ate 11 security rest authorization domain-driven-design

我正在实现一个RESTful服务,其安全模型需要三个级别的授权:

  1. 资源级别授权 - 确定用户是否有权访问资源(实体).例如,当前用户是否有权查看一般客户.如果没有,那么一切都停在那里.
  2. 实例级别授权 - 确定用户是否可以访问资源(实体)的特定实例.由于各种规则和实体的状态,可能不允许当前用户访问该组客户中的一个或多个客户.例如,客户可能能够查看他们自己的信息而不能查看另一个客户的信息.
  3. 属性级别授权 - 确定用户可以在资源(实体)的实例上访问哪些属性.我们有许多业务规则来确定用户是否可以查看和/或更改资源(实体)的各个属性.例如,当前用户可能能够看到客户的姓名,但不能看到他们的地址或电话号码,以及能够查看和添加备注.

实施资源级授权是直截了当的; 但是,其他两个不是.我相信实例级授权的解决方案将通过解决(更难的,imo)属性级授权问题来展示自己.后一个问题很复杂,因为我需要通过属性在响应消息(ala hypermedia)中传递授权决策 - 换句话说,这不是我可以简单地在属性设置器中强制执行的.

对于每个服务请求,我必须使用当前用户的信息来执行这些授权检查.对于资源列表或单个资源的GET请求,我需要告诉API层当前用户可以看到哪些属性(可见)以及该属性是只读还是可编辑.然后,API层将使用此信息来创建适当的响应消息.例如,任何不可见的属性都不会包含在消息中.将标记只读属性,以便客户端应用程序可以为用户呈现处于适当状态的属性.

应用程序服务,方面等解决方案非常适用于资源级别的授权,甚至可以用于实例级别的检查,但我很难确定如何最好地建模我的域,以便包含强制执行安全约束的业务规则和检查.

注意:请记住,这超出了基于角色的安全性,因为我使用资源和环境的当前状态(以及使用通过当前用户授予的权限验证访问权限)获取基于业务规则的最终授权结果他们的角色).

我应该如何建模域名,以便执行所有三种类型的授权检查(以可测试的方式,使用DI等)?

bag*_*rat 2

初步假设

我假设以下架构:

                                     Stateless Scaling                                          Sharding
                                             .                                                     .
                                             .                                                     .
                                             .                                                     .
                         +=========================================+
                         |  Service Layer                          |
+----------+             |  +-----------+        +---------------+ |                         +------------+
|          |    HTTP     |  |           |        |               | |   Driver, Wire, etc.    |            |
|  Client  | <============> |  RESTful  | <====> |  Data Access  | <=======================> |  Database  |
|          |    JSON     |  |  Service  |  DTO   |     Layer     | |     ORM, Raw, etc.      |            |
|          |             |  |           |        |               | |                         |            |
+----------+             |  +-----------+        +---------------+ |                         +------------+
                         +=========================================+
                                             .                                                     .
                                             .                                                     .
                                             .                                                     .
Run Code Online (Sandbox Code Playgroud)

最初,让我们假设您正在使用 进行身份验证ClientService Layer获得特定的令牌,该令牌在其中编码身份验证和授权信息。

首先,我首先想到的是处理所有的请求,然后只根据授权进行过滤。这将使整个事情变得更加简单并且更容易维护。然而,当然可能有一些请求需要昂贵的处理,在这种情况下这种方法绝对无效。另一方面,重负载请求很可能涉及资源级访问,正如您所说,这很容易组织,并且至少可以检测和Service Layer授权APIData Access

进一步的想法

至于实例属性级别的授权,我什至不会尝试将其放入Data Access Layer,并将其完全隔离到 API 级别之外,即从Data Access Layer任何层开始都不会意识到它。即使你请求1M的列表,并希望为该特定客户端发出所有对象的一两个属性,最好是获取整个对象,然后仅隐藏属性。

另一个假设是,您的模型是一个清晰的DTO,即只是一个数据容器,并且所有业务逻辑都在该Service Layer特定API级别中实现。假设您通过 HTTP 传递的数据编码为JSON. 所以无论如何,在该层前面的某个地方API,您将有一个小的序列化阶段,将您的模型转换为JSON. 所以我认为这个阶段是放置实例和属性授权的理想位置。

建议

如果涉及到属性级别的授权,我认为没有合理的方法将模型与安全逻辑隔离。无论是基于规则、基于角色还是基于其他任何方式的授权,该过程都将根据 提供的身份验证/授权令牌中的一个值进行验证Client。因此,在序列化级别,您基本上会得到两个参数,令牌和模型,并相应地序列化适当的属性或整个实例。

当涉及到定义模型的规则、角色和每个属性的任何内容时,可以根据可用的范例以各种方式完成,即根据将Service Layer要实现的语言。可以大量使用注释 (Java)装饰器 (Python)来完成定义。为了发出特定的属性,Python可以通过其动态类型和 hacky 功能(例如Descriptors)来方便地使用。对于Java,您最终可能会将属性封装到模板类中,例如AuthField<T>

概括

综上所述,我建议在序列化阶段API Layer将实例和属性授权放在前面。因此,基本上,角色/规则将在模型中分配,授权将在序列化器中执行,并提供模型和令牌。