Access Control Models – 访问控制模式

最后修改: 2021年 9月 3日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this article, we’ll explore different access control models how to implement them in practice.

在这篇文章中,我们将探讨不同的访问控制模式,如何在实践中实现它们。

2. What’s an Access Control Model?

2.什么是访问控制模型?

A common requirement for applications, especially web-based ones, is that some action can only be performed if a given set of conditions, also referred to as a policy, are satisfied. Ok, this is a very generic requirement, so let’s put forward some examples:

应用程序(尤其是基于 Web 的应用程序)的一个常见需求是,只有在满足一组给定的条件(也被称为政策,)时才能执行某些操作。好吧,这是一个非常通用的需求,所以让我们提出一些例子。

  • Internet Forum: only members can post new messages or reply to existing ones
  • E-commerce site: a regular user can only see his/her own orders
  • Banking back-office: an account manager can manage the portfolio of his/her own clients. In addition to those portfolios, he/she can also manage the portfolio of another account manager’s client when he/she is temporarily unavailable (e.g., vacation) and the former acts as its peer.
  • Digital wallet: payments are limited to $500 from 20:00 to 08:00 in the user’s time zone

The Access Control Model we’ll adopt for a given application will be responsible for evaluating an incoming request and come up with a decision: either the request can proceed or not. In the latter case, the result will usually be an error message that is sent back to the user.

我们将为一个特定的应用程序采用的访问控制模型将负责评估一个传入的请求并作出决定:该请求是否可以继续。在后一种情况下,其结果通常是向用户发送一条错误信息。

Clearly, each of those examples requires a different approach when authorizing a given request.

显然,这些例子中的每一个都需要在授权一个特定的请求时采取不同的方法。

3. Access Control Model Types

3.访问控制模型类型

From the previous examples, we can see that to make an allow/deny decision, we need to take into account different aspects related to the request:

从前面的例子中,我们可以看到,要做出允许/拒绝的决定,我们需要考虑到与请求有关的不同方面。

  • An identity associated with the request. Notice that even anonymous accesses have a form of identity here
  • The objects/resources that are targeted by the request
  • The action performed on those objects/resources
  • Contextual information about the request. Time of day, time zone, and authentication method used are examples of this kind of contextual information

We can categorize Access Control Model into three types:

我们可以把访问控制模型分为三种类型。

  • Role-based Access Control (RBAC)
  • Access Control Lists (ACL)
  • Attribute-based Access Control (ABAC)

Regardless of its type, we can usually identify the following entities in a model:

无论其类型如何,我们通常可以在一个模型中识别以下实体。

  • PEP, or Policy Enforcement Point: Intercepts the request and let it proceed or not based on the result returned by the PDP
  • PDP, or Policy Decision Point: Evaluates requests using a policy to produce an access decision
  • PIP, or Policy Information Point: Stores and/or mediates access to information used by the PDP to make access decisions.
  • PAP, or Policy Administration Point: Manages policies and other operational aspects associated with access decision making.

The following diagram shows how those entities logically relate to each other:

下图显示了这些实体在逻辑上是如何相互关联的。

It is important to note that, although depicted as autonomous entities, in practice, we’ll find that some or even all model elements are embedded in the application itself.

需要注意的是,虽然被描述为自主的实体,但在实践中,我们会发现一些甚至所有的模型元素都被嵌入到应用程序本身中

Also, this model does not address how to establish a user’s identity. Nevertheless, this aspect may be considered when deciding whether to allow a request to proceed.

另外,这个模型没有解决如何建立一个用户的身份。尽管如此,在决定是否允许一个请求继续进行时,可以考虑这方面的问题。

Now, let’s see how we can apply this generic architecture to each of the models above.

现在,让我们看看如何将这一通用架构应用到上面的每个模型中。

4. Role-Based Access Control

4.基于角色的访问控制

In this model, the PDP decision process consists of two steps:

在这个模型中,PDP决策过程包括两个步骤。

  • First, it recovers the roles associated with the identity of the incoming request.
  • Next, it tries to match those roles with the request policy

A concrete implementation of this model is present in the Java EE specification, in the form of the @HttpConstraint annotation and its XML equivalent. This is a typical use of the annotation when applied to a servlet:

该模型的具体实现存在于Java EE规范中,其形式为@HttpConstraint注解及其XML等价物。这是注解在应用于servlet时的一个典型用法。

@WebServlet(name="rbac", urlPatterns = {"/protected"})
@DeclareRoles("USER")
@ServletSecurity(
  @HttpConstraint(rolesAllowed = "USER")
)
public class RBACController  extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Hello, USER");
    }
}

For the Tomcat server, we can identify the access control model entities described before as follows:

对于Tomcat服务器,我们可以确定之前描述的访问控制模型实体如下。

  • PEP: The security Valve that checks for the presence of this annotation in the target servlet and calls the associated Realm to recover the identity associated with the current request
  • PDP: The Realm implementation that decides which restrictions to apply for a given request
  • PIP: Any backend used by a specific Realm implementation that stores security-related information. For the RBAC model, the key information is the user’s role set, usually retrieved from an LDAP repository.
  • Policy Store: In this case, the annotation is the store itself
  • PAP: Tomcat does not support dynamic policy changes, so there’s no real need for one. However, with some imagination, we can identify it with any tool used to add the annotations and/or edit the application’s WEB-INF/web.xml file.

Other security frameworks (e.g., Spring Security) work in a similar fashion. The key point here is that even if a particular framework does not adhere exactly to our generic model, its entities are still there, even if somewhat disguised.

其他安全框架(例如Spring Security)也是以类似的方式工作。这里的关键点是,即使一个特定的框架不完全遵守我们的通用模型,它的实体仍然存在,即使有点伪装。

4.1. Role Definitions

4.1.角色定义

What exactly constitutes a role? In practice, a role is just a named set of related actions a user can perform in a particular application. They can be coarsely or finely defined as required, depending on the application’s requirements.

究竟什么构成了一个角色?实际上,角色只是用户在特定应用中可以执行的一组命名的相关操作。根据应用程序的要求,它们可以被粗略地或精细地定义。

Regardless of their granularity level, it is a good practice to define them, so each one maps to a disjoint set of functionalities. This way, we can easily manage user profiles by adding/removing roles without fearing side effects.

不管它们的粒度是多少,定义它们是一个很好的做法,所以每个角色都映射到一个不相干的功能集。这样一来,我们就可以通过添加/删除角色来轻松地管理用户配置文件,而不用担心会产生副作用。

As for the association between users and roles, we can use a direct or indirect approach. In the former, we assign roles directly to users. In the latter, there’s an intermediate entity, usually a user group, to which we assign roles:

至于用户和角色之间的关联,我们可以使用直接或间接的方法。在前者中,我们直接将角色分配给用户。在后者中,有一个中间实体,通常是一个用户组,我们把角色分配给它。

The benefit of using groups as an intermediary entity in this association is that we can easily reassign roles to many users at once. This aspect is quite relevant in the context of larger organizations, where people are constantly moving from one area to another.

在这种关联中使用组作为中介实体的好处是,我们可以很容易地一次将角色重新分配给许多用户。在大型组织的背景下,这一方面是相当相关的,因为人们不断地从一个区域转移到另一个。

Similarly, the indirect model also allows us to easily change existing role definitions, usually after refactoring an application.

同样,间接模型也允许我们轻松地改变现有的角色定义,通常是在重构一个应用程序之后。

5. Access Control Lists

5.访问控制列表

ACL-based security control allows us to define access restrictions on individual domain objects. This contrasts with RBAC, where restrictions usually apply to whole categories of objects. In the forum example above, we can use an RBAC-only approach to define can read and create new posts.

基于ACL的安全控制允许我们对单个域对象定义访问限制。这与RBAC形成鲜明对比,后者的限制通常适用于整个类别的对象。在上面的论坛例子中,我们可以使用纯RBAC的方法来定义可以阅读和创建新帖子。

However, if we decide to create a new functionality where a user can edit his own posts, RBAC alone will not be enough. The decision engine, in this case, needs to consider not only who but also which post is the target for the editing action.

然而,如果我们决定创建一个新的功能,让用户可以编辑他自己的帖子,仅有RBAC是不够的。在这种情况下,决策引擎不仅需要考虑谁,而且需要考虑哪个帖子是编辑动作的目标。

For this simple example, we can just add a single author column to the database and use it to allow or deny access to the edit action. But what if we wanted to support collaborative editing? In this case, we need to store a list of all people that can edit a post – an ACL.

对于这个简单的例子,我们可以只在数据库中添加一个author列,用它来允许或拒绝对编辑动作的访问。但是,如果我们想支持协作式编辑呢?在这种情况下,我们需要存储一个所有可以编辑帖子的人的列表–ACL。

Dealing with ACLs poses a few practical issues:

处理ACL会带来一些实际问题。

  • Where do we store the ACLs?
  • How to efficiently apply ACL restrictions when retrieving large object collections?

The Spring Security ACL library is a good example of an ACL library. It uses a dedicated database schema and caches to implement ACLs and is tightly integrated with Spring Security. This is a short example adapted from our article on this library showing how to implement access controls at the object level:

Spring Security ACL 库是 ACL 库的一个好例子。它使用专用的数据库模式和缓存来实现 ACL,并与 Spring Security 紧密集成。这是一个改编自我们关于该库的文章的简短示例,展示了如何在对象级别实现访问控制。

@PreAuthorize("hasPermission(#postMessage, 'WRITE')")
PostMessage save(@Param("noticeMessage")PostMessage postMessage);

Another good example of ACLs is the permission system used by Windows to secure objects. Every Securable Object (e.g., files, directories, processes, to name a few) has a Security Descriptor attached to it which contains a list of individual users/groups and the associated permissions:

ACL的另一个好例子是Windows用来保护对象的权限系统。每个可安全的对象(例如,文件、目录、进程,仅举几例)都有一个安全描述符附在其上,其中包含单个用户/群组和相关权限的列表。

Windows ACLs are quite powerful (or complicated, depending on who we ask), allowing administrators to assign permissions to individual users and/or groups. Furthermore, individual entries define allow/deny permissions for each possible action.

Windows ACL是相当强大的(或复杂的,取决于我们问谁),允许管理员为单个用户和/或组分配权限。此外,个别条目为每个可能的行动定义了允许/拒绝的权限。

6. Attribute-Based Access Control

6.基于属性的访问控制

Attribute-based control models allow access decisions based not only on the identity, action, and target object but also on contextual information related to a request.

基于属性的控制模型允许不仅根据身份、行动和目标对象,而且根据与请求有关的上下文信息做出访问决定

The XACML standard is perhaps the most well-known example of this model, which uses XML documents to describe access policies. This is how we can use this standard to describe the digital wallet withdrawal rule:

XACML标准也许是这种模式的最著名的例子,它使用XML文档来描述访问策略。我们可以这样使用这个标准来描述数字钱包的提款规则。

<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" 
  PolicyId="urn:baeldung:atm:WithdrawalPolicy"
  Version="1.0" 
  RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides">
    <Target/>
    <Rule RuleId="urn:oasis:names:tc:baeldung:WithDrawalPolicy:Rule1" Effect="Deny">
        <Target>
            <AnyOf>
                <AllOf>
... match rule for the withdrawal action omitted
                </AllOf>
            </AnyOf>
        </Target>
        <Condition>
... time-of-day and amount conditions definitions omitted
        </Condition>
    </Rule>
</Policy>

The full rule definition is available online.

完整的规则定义可在在线上查阅。

Despite its verbosity, it’s not hard to figure out its general structure. A policy contains one or more rules that, when evaluated, results in an Effect: Permit or Deny.

尽管它很冗长,但要弄清它的一般结构并不难。一个策略包含一个或多个规则,当评估时,产生一个Effect。允许或拒绝

Each rule contains targets, which define a logical expression using attributes of the request. Optionally, a rule can also contain one or more Condition elements that define its applicability.

每个规则都包含目标,这些目标使用请求的属性定义一个逻辑表达。当然,一个规则也可以包含一个或多个定义其适用性的Condition元素。

At runtime, an XACML-based access control PEP creates a RequestContext instance and submits it to the PDP for evaluation. The engine then evaluates all applicable rules and returns the access decision.

在运行时,基于XACML的访问控制PEP创建一个RequestContext实例,并将其提交给PDP进行评估。然后,该引擎评估所有适用的规则,并返回访问决定。

The kind of information present in this RequestContext is the main aspect that differentiates this model from the preceding ones. Let’s take, for example, an XML representation of a request context built to authorize a withdrawal in our digital wallet application:

这个RequestContext中的信息种类是这个模型与前面的模型不同的主要方面。让我们举个例子,在我们的数字钱包应用程序中,为授权提款而建立的请求上下文的XML表示。

<Request 
    xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
    CombinedDecision="true"
    ReturnPolicyIdList="false">
    
    <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action">
... action attributes omitted
    </Attributes>
    <Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:environment">
... environment attributes (e.g., current time) omitted
    </Attributes>
    <Attributes Category="urn:baeldung:atm:withdrawal">
... withdrawal attributes omitted 
    </Attributes>
</Request>

When we submit this request to the XAML rule evaluation engine at 21:00, the expected result will be the refusal of this withdrawal, as it exceeds the maximum allowed amount for nightly transactions.

当我们在21:00向XAML规则评估引擎提交这个请求时,预期的结果将是拒绝这个提款,因为它超过了夜间交易的最大允许金额。

The key advantage of the ABAC model is its flexibility. We can define and, more importantly, modify complex rules simply by changing the policy. Depending on the implementation, we can even do it in real-time.

ABAC模型的关键优势在于其灵活性。我们可以定义复杂的规则,更重要的是,仅仅通过改变策略来修改复杂的规则。根据实施情况,我们甚至可以实时进行修改。

6.1. XACML4J

6.1 XACML4J

XACML4J is an open-source implementation of the XACML 3.0 standard for Java. It provides an evaluation engine and related entities’ implementations required by the ABAC model. Its core API is the PolicyDecisionPoint interface, which, not surprisingly, implements the PDP logic.

XACML4J是XACML 3.0标准在Java中的一个开源实现。它提供了ABAC模型所需的评估引擎和相关实体的实现。它的核心API是PolicyDecisionPoint接口,不出所料,它实现了PDP逻辑。

Once we’ve built the PDP, using it requires two steps. First, we create and populate a RequestContext with information about the request we want to evaluate:

一旦我们建立了PDP,使用它需要两个步骤。首先,我们创建并填充一个RequestContext,其中包含我们要评估的请求的信息。

... attribute categories creation omitted
RequestContext request = RequestContext.builder()
  .attributes(actionCategory,environmentCategory,atmTxCategory)
  .build();

Here, each xxxCategory parameter contains a set of attributes for the associated Category. The full code uses the available builders to create a test request for a withdrawal of $1,200.00 happening at 21:00. Alternatively, we can also create a RequestContext object directly from any JAXB-compatible source.

在这里,每个xxxCategory参数包含一组相关Category的属性。完整的代码使用可用的构建器来创建一个测试请求,即在21:00发生1,200.00美元的提款。另外,我们也可以直接从任何JAXB兼容的源创建一个RequestContext对象。

Next, we pass this object to the decide() method of the PolicyDecisionPoint service for its evaluation:

接下来,我们将这个对象传递给decide()服务的PolicyDecisionPoint方法进行评估。

ResponseContext response = pdp.decide(request);
assertTrue(response.getDecision() == Decision.DENY); 

The returned ResponseContext contains a Decision object with the policy evaluation result. Additionally, it may also return diagnostic information and additional obligations and/or advice to the PEP. Obligations and advice are a topic on themselves, so we won’t cover them here. This tutorial from Axiomatic shows how we can use this feature to implement regulatory controls in a typical system of records application.

返回的ResponseContext包含一个包含政策评估结果的Decision对象。此外,它还可能返回诊断信息和额外的义务和/或建议给PEP。义务和建议本身就是一个话题,所以我们在此不做介绍。来自Axiomatic的教程展示了我们如何使用该功能在一个典型的记录系统应用中实施监管控制。

6.2. ABAC Without XACML

6.2.没有XACML的ABAC

The complexity of XACML usually makes it overkill for most applications. However, we can still use the underlying model in our applications. After all, we can always implement a simpler version tailored to a specific use-case, maybe externalizing just a few parameters, right?

XACML的复杂性通常使其对大多数应用来说都是多余的。然而,我们仍然可以在我们的应用中使用底层模型。毕竟,我们总是可以针对特定的用例实现一个更简单的版本,也许只需外部化一些参数,对吗?

Well, any seasoned developer knows how this ends…

好吧,任何经验丰富的开发者都知道这个结局……。

A tricky aspect of any ABAC implementation is how to extract attributes from the request’s payload. Standard methods to insert custom logic before processing a request, such as servlet filters or JAX-RS interceptors have only access to raw payload data.

任何ABAC实现的一个棘手的方面是如何从请求的有效载荷中提取属性。在处理请求之前插入自定义逻辑的标准方法,如servlet过滤器或JAX-RS拦截器,只能访问原始有效载荷数据。

Since modern applications tend to use JSON or similar representations, the PEP must decode it before it can extract any payload attribute. This means a potential hit on CPU and memory usage, especially for large payloads.

由于现代应用程序倾向于使用JSON或类似的表示方法,PEP必须在提取任何有效载荷属性之前对其进行解码。这意味着对CPU和内存使用的潜在打击,特别是对于大型有效载荷。

In this case, a better approach is to use AOP to implement the PEP. In this case, the aspect handler code has access to the decoded version of the payload.

在这种情况下,一个更好的方法是使用AOP来实现PEP。在这种情况下,方面处理程序代码可以访问有效载荷的解码版本。

7. Conclusion

7.结语

In this article, we’ve described different access control models and how applications use them to enforce access rules.

在这篇文章中,我们已经描述了不同的访问控制模型,以及应用程序如何使用它们来执行访问规则。

As usual, the full source code of the examples can be found over on GitHub.

像往常一样,这些例子的完整源代码可以在GitHub上找到