转自:www.liangzl.com/get-article…
译自官方文档,原文链接。对应Keycloak版本为5.0。
概述
Keycloak支持细粒度的授权策略,并可以对这些策略进一步组合,如:
- 基于属性(ABAC)
- 基于角色(RBAC)
- 基于用户(UBAC)
- 基于上下文(CABC)
- 基于规则(Rule-based)
- 通过JavaScript
- 使用Jboss Drools
- 基于时间(Time-based)
- 通过SPI自定义访问控制策略(ACM)
Keycloak提供了一组管理界面和RESTful API,用于创建权限、关联权限与授权策略,以及在应用程序中执行授权决策。
资源服务器需要依据一些信息才能判断权限。对于REST的资源服务器这些信息通常来自于一个加密的token,如在每个访问请求中携带bearer token。对于依赖于session的Web应用来说,这些信息就来自于每个request对应的用户session。
资源服务器通常执行的是基于角色的访问控制(RBAC)策略,即检查用户所拥有的角色是否关联了要访问的资源。虽然这种方式非常有用,但是它们也有一些限制:
- 资源和角色紧耦合,角色的更改(如添加、删除或更改访问上下文)可能影响多个资源。
- 基于RBAC的应用程序无法很好地响应安全性需求的调整。
- 项目规模扩大时,复杂的角色管理会很困难而且容易出错。
- 不够灵活。角色并不能有效代表用户身份,即缺乏上下文信息。客观上来说被授予了角色的用户,至少会拥有某些访问权限。
当下的项目中,我们需要考虑不同地区、不同本地策略、使用不同设备、以及对信息共享有较高需求的异构环境。Keycloak授权服务可以通过以下方式帮助您提高应用程序和服务的授权能力:
- 不同的访问控制机制以及细粒度的授权策略。
- 中心化的资源、权限以及策略管理。
- 中心化的策略决策。
- REST风格的授权服务。
- 授权工作流以及用户访问管理。
- 可作为快速响应您项目中安全需求的基础设施。
架构
从设计的角度来看,授权服务基于一组授权模式,提供了以下功能:
- 策略管理点(PAP)
在Keycloak Admin的基础上提供了UI界面来管理资源服务器、资源、范围、权限以及策略。其中的部分功能也可以通过Protection API来实现。
- 策略决策点(PDP)
为和权限请求相应的授权请求以及策略评估,提供了分布式的策略决策点。更多信息请查看获取权限章节。
- 策略执行点(PEP)
为资源服务器端实际执行授权决策,Keycloak提供了用于不同环境的一些内置策略执行器。
- 策略信息点(PIP)
基于Keycloak的认证服务器,可以在策略评估时从身份认证信息以及上下文环境中获取一起其它属性。
授权过程
Keycloak的细粒度授权主要有三个必要步骤:
- 资源管理
- 权限及策略管理
- 执行策略
资源管理
资源管理定义了什么是被保护的对象。
首先,需要定义被保护的资源服务器,通常是一个web应用或一组服务。有关资源服务器的更多信息请参考术语。
资源服务器可以使用Keycloak管理员控制台来管理。在那里,您可以将任何已注册的客户端启用为资源服务器,并管理其资源和范围。
资源可以是web页面、Rest资源、文件系统上的一个文件、一个EJB等等。它们可以是一组资源(如Java中的一个Class),也可以是一个特定的资源。
举例来说,你可以用Bank Account来代表所有的银行账户,并且用它来定义对全部银行账户的通用授权。但同时你也可以为Alice的私有账户来单独设置权限,使得只有Alice本人能够对其账户进行某些操作。
资源可以通过Keycloak控制台或者Protection API来管理。利用Protection API的方式,资源服务器可以远程管理资源。
范围(Scope)通常用来表示在资源上执行的动作,但是这不是它唯一的用途。比如你可以使用范围来代表资源中的一个或者多个属性。
权限及策略管理
定义好了资源服务器和其上的资源,下一步就是定义权限和策略。
定义策略的步骤图示如下:
策略定义了在资源或者范围上执行某些动作的先决条件,但是请记住它们并不和被保护的资源直接绑定。策略是通用的,可以通过再次组合来构造更复杂的权限及策略。
举例来说,要获取“User Premium”角色下的资源组访问权限,你可以使用RBAC策略。
Keycloak为常用场景提供了一些内置的策略类型。你可以使用JavaScript或者JBoss的Drools来编写自己的策略。
定义了策略之后就可以来定义权限了。权限与被保护的资源紧耦合,它们由被保护对象及策略组合而成。
执行策略
策略的执行包含了对资源服务器实际实施授权决策的必要步骤。实现方式是在资源服务器上启用策略实施点(PEP),PEP能够与授权服务器通信,请求授权数据,并根据服务器返回的决策来控制对受保护资源的访问。
Keycloak提供了一些内置的PEP实现,你可以根据项目运行平台来自由选择。
授权服务
授权服务包括下列三种REST接口:
- Token Endpoint
- Resource Management Endpoint
- Permission Managerment Endpoint
上边的各个endpoint涵盖了授权服务的各个步骤。
Token Endpoint
Oauth2客户端(如前端应用)可以通过token endpoint获取访问令牌(access token),然后使用这些令牌来获取资源服务器(如后端应用)上被保护的资源。同样,Keycloak授权服务扩展了OAuth2,允许基于配置好的策略发放访问令牌。这意味着资源服务器可以利用关联了权限的令牌来对资源进行保护。在Keycloak中,带有权限的访问令牌被称作请求方令牌(Requesting Party Token),或者缩写为RPT。
更多的信息请查看获取权限章节。
保护API
保护API指提供的一系列UMA兼容的操作,用来帮助资源服务器管理资源、范围、权限及相关策略。只有资源服务器才能被允许访问这些API,前提资源服务器有内置的uma_protection范围。
提供给资源服务器的API可以分为两组:
- 资源管理
创建资源、删除资源、根据ID查询、其他查询
- 权限管理
发放权限Ticket
远程资源管理功能默认开启。可以通过Keycloak管理员控制台来关闭此功能。
如果使用了UMA协议,Protection API对权限Ticket的发放是整个授权流程的重要组成部分。后面会讲到,它们表示着在整个授权过程中客户端所申请的权限及向服务器申请包含着权限的最终令牌。
更多信息请查看保护API章节。
名词解释
在深入到Keycloak前我们需要解释一下Keycloak授权服务中所用到的专有名词。
资源服务器(Resource Server)
如前所述,资源服务器通常依赖某种信息来决定是否授权。该授权信息通常包含在安全令牌或用户会话中。
任何可信的Keycloak客户端都可以作为资源服务器。这些客户端的资源以及范围由一系列的授权策略保护。
资源
资源是应用或者组织的资产。它们可以是一些列的端点、一个典型的HTML页面等等。在授权策略语境中,资源指的就是被保护的对象。
每个或者每组资源都有着唯一标识。请回想上文的Bank例子。
范围
资源的范围扩展了资源的访问界限。在授权语义中,范围是可以应用在资源上的许多动词之一。
它通常表示对指定资源可施加的动作。如查看、编辑、删除等等。但是范围也可以表示资源的其它关联信息。如工程资源和造价范围,这里的造价范围被用来定义用户访问工程造价的特定策略及权限。
权限
权限将被保护的对象与必须评估的策略关联起来,以确定是否允许访问。
- X 可以在资源 Z 上施加 Y
这里X指可以是用户、角色、用户组,或者其组合。在这里你可以使用声明和上下文。
Y指代一个动作,如写、读等等。
Z指代背标户的资源,如 "/accounts"
Keycloak提供了丰富的平台用于构建从简单到复杂的权限策略,如基于规则的权限控制。使用Keycloak可以带来:
- 减少代码重构和权限管理成本。
- 支持灵活的安全模型,用以应对安全模型可能的变化。
- 支持运行时变化。应用系统只需要关心资源和范围,Keycloak隐藏了它们被保护的细节
策略
策略定义了授予对象访问权时必须满足的条件。与权限不同,策略不指定受保护的对象,而是指定访问制定对象(如资源、范围或两者)时必须满足的条件。策略与用来保护资源的访问控制机制(ACMs)密切相关。使用策略可以实现基于属性的访问控制(ABAC)、基于角色的访问控制(RBAC)、基于上下文的访问控制以及其任何组合。
Keycloak还提供了聚合策略,即“策略的策略”。在构造复杂的访问控制条件时,Keycloak授权服务遵循了分而治之的原则。你可以创建独立的策略,并在不同的权限中使用它们,然后通过组合它们来构造更复杂的策略。
策略提供者(Policy Provider)
策略提供者是特定策略类型的具体实现。Keycloak提供的内置策略都是由不同的提供者来支持的,也允许添加自定义的策略类型。
Keyclaok提供了SPI(Service Provider Interface),你可以使用SPI来添加自定义策略提供者。
权限许可(Permission Ticket)
权限许可是由UMA定义的特殊token类型,它的不透明结构由授权服务器所决定。这种结构代表了客户端请求的资源或者范围、访问上下文,以及要执行的策略(RPT)。
在UMA中,权限许可是人与人、人与组织之间共享的关键。在授权工作流中使用许可票据可以支持从简单到复杂的一系列场景,让资源所有者和资源服务器细粒度地控制资源访问。
在UMA工作流中,授权服务器向资源服务器发出权限许可,资源服务器将许可返回给试图访问受保护资源的客户端。客户端拿到许可就可以通过将它发送回授权服务器来请求RPT(最终的包含授权数据的令牌)。
开始
在使用本教程之前,请确保正确安装了Keycloak并已经初始化了管理员账户。注意,你必须在Keycloak同一台主机上运行独立的WildFly实例。这个独立的实例将用来运行你的Java Servlet应用。你可以在启动命令中使用jboss.socket.binding.port-offset来避免端口冲突。
可以用如下命令来启动Keycloak服务(Linux版):
$ .../bin/standalone.sh -Djboss.socket.binding.port-offset=100
Windows版:
> ...\bin\standalone.bat -Djboss.socket.binding.port-offset=100
更多安装配置WildFly的细节请查看这里。
正确安装启动之后,可以从 http://localhost:8180/auth/dmin 访问Keycloak控制台,以及从 http://localhost:8080 访问WildFly实例。
保护Servlet应用
本入门指南旨在让您尽快启动、运行及测试Keycloak提供的各种授权特性。它主要依赖于默认的数据库和服务器配置,并不涉及复杂的部署选项。有关特性或配置选项的更多信息,请参阅本文档中的相关部分。
本指南阐述了Keycloak授权服务的关键概念:
- 为客户应用提供细粒度的授权
- 将客户应用设置为资源服务器并保护其资源
- 定义权限及授权策略来保护资源
- 在应用中启用PEP
创建域(Realm)及用户
首先来创建域及域中的用户。然后在域中创建客户应用,即需要保护的资源服务器。
依照以下步骤来创建域及用户:
1. 创建名为 hello-world-authz 的域。创建成功之后的页面如下图:
2. 创建用户。点击 Users,打开用户列表页。
3. 在右侧空白的用户列表页点击 Add User。
4. 填写Username、Email、First Name及Last Name,打开User Enabled选项,然后点击 Save。
5. 在Credentials选项卡修改用户密码。
6. 填写New Password、及Password Confirmation,然后关闭Temporary。
7. 点击Reset Password完成设置用户密码。
启用授权服务
你可以为已有的客户端应用开启OpenID Connect协议。也可以创建一个新的客户端。
依照以下步骤来创建新客户端:
1. 点击Clients来创建一个新的应用,并填写Client ID,Client Protocol,以及Root URL。
2. 点击Save。
3. 开启Authorization Enabled开关,会打开新的Authorization选项卡。
4. 点击Authorization选项卡:
一旦为客户端应用开启了授权服务,Keycloak会自动创建一些授权默认配置。
有关授权配置的更多信息,请查看开启服务授权章节。
构建、部署及测试
到这里,app-authz-vanilla资源服务器应该已经被正确配置完毕了,现在我们开始部署。项目可以在这里直接下载。部署及构建的前置要求如下:
- Java JDK 8
- Apache Maven 3.1.1或更高版本
- Git
项目基于最新的Keycloak版本。可以使用如下命令来克隆代码:
$ git clone https://github.com/keycloak/keycloak-quickstarts
要构建的项目地址在:
$ cd keycloak-quickstarts/app-authz-jee-vanilla
获取适配器配置
构建项目之前请依照以下步骤来获取适配器配置。
1. 点击Clients。在client列表中,点击app-authz-vanilla。
2. 点击 Installation 选项卡。在下拉列表中选中Keycloak OIDC JSON格式。然后点击Download。
3. 将keycloak.json移到app-authz-jee-vanilla/config目录。
4. 默认情况下,访问无权限的资源时Policy执行器会返回403。你也可以定义一个重定向页面。要定义重定向页面需要修改第3步中keycloak.json文件的policy-enforcer配置:
"policy-enforcer": {
"on-deny-redirect-to" : "/app-authz-vanilla/error.jsp"
}
上面的配置将403重定向到一个错误页面。
构建及部署应用
执行下面的命令来构建、部署应用:
$ cd redhat-sso-quickstarts/app-authz-jee-vanilla
$ mvn clean package wildfly:deploy
测试应用
如果成功部署了应用,你可以从 http://localhost:800/app-authz-vanilla来访问Keycloak登录页。
使用刚创建的alice用户登录。
在为客户端启用授权服务时,Keycloak默认提供了一个简单的策略,即授予所有该策略保护的资源以访问权限。
现在你可修改默认的权限及策略,然后来测试程序的响应,或者使用不同的策略类型来创建新的策略。
可以点击Authorization选项卡中的Policies选项卡,然后点击Default Policy来修改:
// The default value is $evaluation.grant(),
// let's see what happens when we change it to $evaluation.deny()
$evaluation.deny();
现在,退出demo应用并重新登录。你会发现无法访问它了:
现在我们不更改默认的策略代码,而是将策略代码文本区域下方的Logic更改为Negative。这将重新启用对应用程序的访问,默认情况下,该策略拒绝所有访问请求。同样,在测试此更改之前请记得注销并重新登录。
下一步
我们还可以:
- 创建一个范围,定义一个策略及它的权限,然后在应用端来进行测试。看用户是否能够执行动作(或者你刚刚创建的范围)?
- 创建不同类型的策略,如基于规则的,并将这些策略和默认权限关联起来。
- 对默认权限应用多个策略并测试。例如,组合多个策略并相应地更改决策策略(Decision Strategy)。
- 关于查看及测试权限的更多信息,可以查看获取授权上下文章节。
授权快速入门
除了app-authz-jee-vanilla快速开始指南,Keycloak Quickstarts仓库中也包含本文档中的其他例子。
授权快速开始指南只是一个起点,用于帮助你快速浏览在各种场景下keycloak提供的不同技术以及集成方式。它并不是一个全面的用户手册。
每个快速开始的项目都有一个README文档来介绍如何构建、部署及测试。下表是一个简单的介绍:
Name | Description |
---|---|
app-authz-jee-servlet | 介绍在Java EE项目中如何精细化管理权限,并基于从Keycloak服务器获取到的权限来动态构建菜单 |
app-authz-jee-vanilla | 介绍在Java EE项目中如何精细化管理权限,并使用默认的授权设置来保护资源 |
app-authz-rest-springboot | 介绍如何使用Keycloak授权服务来保护SpringBoot项目 |
app-authz-springboot | 介绍如何用切面来认证并授权SpringBoot REST服务 |
app-authz-uma-photoz | 这是一个简单的基于HTML5+AngularJS+JAX-RS的项目,用来展示如何使用UMA来让用户管理自己的权限 |
管理资源服务器
根据OAuth2规范,资源服务器托管资源,并接受和响应对受保护资源的请求。
Keycloak为资源服务器提供了一个丰富的平台,用于为受保护的资源启用细粒度授权,并可以根据不同的访问控制机制来决策授权。
可以为任意客户端开启细粒度授权。这样做的时候你其实已经将一个客户端当做了一个资源服务器。
创建客户端应用程序
要开启Keycloak授权服务,必须先创建一个资源服务器。
1. 点击Clients
2. 在上面的页面中点击 Create。
3. 输入Client ID。如my-resource-server。
4. 输入Root URL。如:
http://${host}:${port}/my-resource-server
5. 点击 Save。然后开始配置client设置:
开启授权服务
将Authorization Enabled开关设置为ON并保存,就可以将OIDC 客户端设为一个资源服务器并开启细粒度授权。
上面的操作会打开新的Authorization选项卡:
Authorization选项卡下方会带有子选项卡。我们后边会讲解每个子选项卡的作用。先来快速浏览一下主要功能:
- Settings
包含资源服务器的常规设置。更多细节请查看这里。
- Resource
在这里可以管理客户端应用的资源。
- Authorization Scopes
在这里管理范围。
- Policies
在这里管理策略,以及定义授权时必须满足的条件。
- Permissions
在这里可以通过将资源/范围和策略关联起来来设置它们的权限。
- Evaluate
在这里模拟授权场景,并观察结果是否和预期一致。
- Export Settings
在这里可以导出授权配置到JSON文件。
资源服务器设置
在资源服务器设置页面可以设置策略执行模式,是否允许远程资源管理,并可以导出授权配置。
- 策略执行模式
- 强制 默认模式,对没有关联策略的资源的访问会直接被拒绝。
- 自由 对没有关联到策略的资源的访问会被允许。
- 禁用 禁用所有策略的评估,并直接允许访问。
- 远程资源管理
定义资源是否可被远程管理,如果关闭,资源仅能在管理员控制台管理。
默认配置
创建好了资源服务器,Keycloak会为其自动创建默认配置。
默认配置包括:
- 一个默认的受保护资源(通配符),它代表着项目中全部资源。
- 一个默认策略,始终授予对受此策略保护的资源的访问权(即默认全部授权)。
- 一个默认权限,它基于默认策略来管理对所有资源的访问的权限。
这个默认的受保护资源被称为默认资源,导航到Resources选项卡就可以看到它:
这个资源定义了一个名为 urn:my-resource-server:resources:default 的类型,以及一个 /* 的URI。这里的URI以通配符的形式匹配着项目中的全部路径。换句话说,当为应用程序启用策略强制时,将在授予访问权之前检查与资源关联的所有权限。
上面所说的类型可以用来被用来创建基于类型的资源权限,来管理属于同一类型的资源。
现在可以在Policies选项卡界面查看默认的realm策略。
此策略是一个基于JavaScript的策略,此策略定义了永远无条件授权。如果点击了策略你可以看到它定义的规则如下:
// by default, grants any permission associated with this policy
$evaluation.grant();
最后来看默认权限,如果导航到Permissions选项卡,就可以查看它:
此权限是一个基于资源的权限,定义了一组策略(一个或多个)应用于具有给定类型的所有资源。
修改默认配置
可以通过先删除后新建的方式来修改默认配置。
注意默认的资源以通配符的形式匹配着所有路径,在新建时不要产生冲突。
默认配置定义了一个映射到应用程序中的所有路径的资源。如果要对自己的资源写入权限,请确保删除默认资源或将其URI字段更改为应用程序中更特定的路径。否则,与默认资源关联的策略默认情况下总是授予访问权。
导出导出授权配置
资源服务器的配置可以自由导入导出。当你希望为资源服务器创建初始配置或更新现有配置时这项功能非常有用。导出的配置包含:
- 被保护的资源和范围
- 策略
- 权限
导出
遵循以下步骤导出配置:
1. 进入Resource Server Settings 页面。
2. 点击Export Settings选项卡。
3. 点击Export按钮。
导出的文件是JSON格式,导出前会在text area中展示,你也可以选择从这里复制粘贴它。
导入配置文件
依照以下步骤来导入配置文件。
1. 导航到Resource Server Settings页面。
通过Select file选择要导入的配置然后加载。
管理资源和范围
资源管理功能相当直观。只要创建了资源服务器,就可以创建要保护的资源和范围。可以从Resource和Scope选项卡来管理它们。
查看资源
在Resouce页面,你可以查看当前资源服务器上的资源列表。
资源列表展示了被保护的资源的:
- 类型
- URIS
- 所有者
- 关联的范围(如果存在)
- 关联的权限
你可以从列表中选择资源点击Create Permission来为其创建权限。
创建权限前请确保已定义好需要关联的策略。
创建资源
主要需要考虑的是资源的粒度。Keycloak的Resource概念可以用来表示一组(一个或多个)资源,如何定义它们对于管理权限非常重要。
要创建新的资源,请点击资源列表页面的右上角Create按钮。
在Keycloak中,资源定义了一组通用的信息,如:
- Name
可读的、全局唯一的字符串
- Type
唯一标识一组资源类型的字符串,用来对不同资源实例分组。如自动创建的默认资源的默认类型是 urn:resource-server-name:resources:default
- URIS
代表着资源的地址。对于HTTP资源来说,URIS通常是资源的相对路径。
- 范围
资源关联的一个或多个范围。
资源属性
资源可能具有与其相关联的属性。属性可在评估权限时用于向策略提供附加信息。
每个属性都是一个键和值对,值可以是字符串集合。属性的多个值可以用逗号加以区分。
分类资源
资源的type字段可用于将不同的资源归类,从而可以使用一组公共权限来保护它们。
资源所有者
资源都有一个拥有者。默认情况下资源属于资源服务器。
但资源也可以属于用户,从而让你创建用户相关的策略。如仅有资源拥有者可以更新或者删除指定的资源。
资源的远程管理
资源远程管理接口通过Protection API暴露。
当使用Protection API时,可以让资源服务器管理来用户的资源。此时可以指定用户ID来将资源设置为属于特定用户。
Keycloak支持资源服务器对其资源的完全控制。将来Keycloak计划允许用户控制自己的资源,以及批准授权请求和管理权限,特别是使用UMA协议时。
管理策略
如前所属,策略用来定义授权前需要满足的特定条件。
可以浏览Policy选项卡来查看当前资源服务器上的所有策略:
你可以在这里创建及编辑策略。
要创建新的策略,请从右上角的Create policy下拉列表中选择一个策略类型。接下来我们将详细描述每种策略类型。
基于用户的策略
基于用户的策略可以限制只有指定的用户能够访问被保护的资源。
要创建新的基于用户的策略,请在新建策略时选中User选项。
配置
- 名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
- 描述
此策略的详情
- Users
指定哪些用户可以被此策略授权。
- 逻辑
在评估了此策略相关条件之后再应用的逻辑。
基于角色的策略
你可以使用这种类型的策略来定义允许一组角色访问对象的条件。
默认情况下,如果发起请求的用户已被授予此策略中任意一个角色就可以获得授权。也可以根据需要指定特定的必要角色,没有必要角色的用户将不会获得授权。可以自由组合必需角色和非必需角色,包括领域角色以及客户端角色。
要创建基于角色的策略,请在新建策略时选中Role。
配置
- 名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
- 描述
此策略的详情
- Realms Roles
指定哪些realm角色被此策略授权。
- Client Roles
指定哪些client角色被此策略授权。要启用客户端角色需要首先选择一个Client。
- 逻辑
在评估了此策略相关条件之后再应用的逻辑。
定义必要角色
如前所述可以将策略中的角色指定为必要角色。仅当用户被授予了所有必要角色时,才会被授权。必要角色可以是realm角色,也可以是client角色。
若要按需指定角色,请选中角色后面的Required复选框。
必需的角色的意义在于:你的策略定义了多个角色,但只有某个子集是强制性的。在这种情况下,你可以通过组合realm和client角色来启用更细粒度的RBAC。例如可以为客户端订制特定的策略来要求指定的客户端角色。或者仅在特定realm角色存在时才授予访问权限。你可以在同一个策略中组合这两种方法。
基于JavaScript的策略
这种策略允许你编写JavaScript脚本来定义条件。它是Keycloak支持的基于规则的策略类型(rule-based)之一,它基于评估API几乎可以提供任意的灵活性。
要创建基于JS的策略,请在创建策略时选中JavaScript。
配置
- 名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
- 描述
此策略的详情
- Code
此策略的JavaScript脚本。
- 逻辑
在评估完此策略相关条件之后再应用的逻辑。
例子
检查来自评估上下文的属性
这是一个基于JS来实现基于属性的权限控制(ABAC)的例子,其中的属性从执行上下文环境中获取:
var context = $evaluation.getContext();
var contextAttributes = context.getAttributes();
if (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {
$evaluation.grant();
}
检查请求方身份的属性
下面基于ABAC的小例子展示了如何检查申请方的身份:
var context = $evaluation.getContext();
var identity = context.getIdentity();
var attributes = identity.getAttributes();
var email = attributes.getValue('email').asString(0);
if (email.endsWith('@keycloak.org')) {
$evaluation.grant();
}
代码中所用到的属性映射自授权请求携带的token中预先好定义的声明。
检查请求方的角色
下面的代码展示如何检查用户是否被授予了keycloak_user的域角色:
var context = $evaluation.getContext();
var identity = context.getIdentity();
if (identity.hasRealmRole('keycloak_user')) {
$evaluation.grant();
}
也可以检查用户是否被授予了my-client-role 的客户端角色,其中my-client是客户机应用程序的客户机id
var context = $evaluation.getContext();
var identity = context.getIdentity();
if (identity.hasClientRole('my-client', 'my-client-role')) {
$evaluation.grant();
}
检查角色是否授予了一个用户
下面的代码检查指定的域角色是否授予了一个用户:
var realm = $evaluation.getRealm();
if (realm.isUserInRealmRole('marta', 'role-a')) {
$evaluation.grant();
}
下面检查指定的客户端角色是否授予了一个用户:
var realm = $evaluation.getRealm();
if (realm.isUserInClientRole('marta', 'my-client', 'some-client-role')) {
$evaluation.grant();
}
检查角色是否授予了一个组
var realm = $evaluation.getRealm();
if (realm.isGroupInRole('/Group A/Group D', 'role-a')) {
$evaluation.grant();
}
推送任意声明到服务器
可以向资源服务器推送任意声明,以便提供应如何执行权限的附加信息:
var permission = $evaluation.getPermission();
// decide if permission should be granted
if (granted) {
permission.addClaim('claim-a', 'claim-a');
permission.addClaim('claim-a', 'claim-a1');
permission.addClaim('claim-b', 'claim-b');
}
检查用户是否属于某个组
var realm = $evaluation.getRealm();
if (realm.isUserInGroup('marta', '/Group A/Group B')) {
$evaluation.grant();
}
使用多重访问控制机制
你可以自由组合多种访问控制机制。下面的例子展示了在同一个策略中组合RBAC和ABAC,它将检查user是否有admin角色,并且其邮箱是否属于keycloak.org域:
var context = $evaluation.getContext();
var identity = context.getIdentity();
var attributes = identity.getAttributes();
var email = attributes.getValue('email').asString(0);
if (identity.hasRealmRole('admin') || email.endsWith('@keycloak.org')) {
$evaluation.grant();
}
在编写自己的规则时,请记住 $evaluate对象实现了org.keycloak.authority.policy.evaluate.evaluation。有关该接口的更多信息,请参见Evaluation API。
基于规则的策略
Drools策略目前仅仅是一个预览版而不是支持的。默认情况下该功能被禁用。
要启用,请在服务器启动命令中添加 -Dkeycloak.profile=preview 或 -Dkeycloak.profile.feature.authz_drools_policy=enabled。更多细节请查看这里。
使用这种类型的策略,你可以使用Drools来定义权限的条件。Drools可以视作一个规则评估环境。它是Keycloak支持的基于规则的策略类型之一,同样提供了编写任何策略的灵活性。
要创建基于规则的策略,请在创建策略的下拉列表中选择Rule。
配置
- 名称
名称是一个可读的字符串。我们强烈建议取一些和业务相关的名字以易于识别。
- 描述
此策略的详情。
- Policy Maven Artifact
这里需要提供一个Maven的GAV坐标,然后点击Resolve来加载Module和Session。
- Group Id
- Artifact Id
- Version
- 逻辑
在评估完此策略相关条件之后再应用的逻辑。
例子
下面是一个ABAC的Drools小例子,仅当申请方为资源拥有者时才授权。
import org.keycloak.authorization.policy.evaluation.Evaluation;
rule "Authorize Resource Owner"
dialect "mvel"
when
$evaluation : Evaluation(
$identity: context.identity,
$permission: permission,
$permission.resource != null && $permission.resource.owner.equals($identity.id)
)
then
$evaluation.grant();
end
也可以从身份中获取一个属性并加以判断:
import org.keycloak.authorization.policy.evaluation.Evaluation;
rule "Authorize Using Identity Information"
dialect "mvel"
when
$evaluation : Evaluation(
$identity: context.identity,
identity.attributes.containsValue("someAttribute", "you_can_access")
)
then
$evaluation.grant();
end
更多关于Evaluation接口的信息请查看评估API。
基于时间的策略
此种策略允许你制定时间相关的权限条件。
要创建基于时间的策略,请在创建策略的下拉列表中选中Time。
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
Not Before
在此时间前不得授权。只有当前日期/时间晚于或等于此值时才授权。
Not On or After
在此时间后不得授权。只有当前日期/时间早于此值时才授权。
Day of Month
定义必须授予访问权限的日期。允许指定日期范围。此时,只有日期介于或等于指定的两个值之间时,才会授予权限。
Month
定义必须授予访问权限的月份。允许指定月范围。此时,只有当月份介于指定的两个值之间时,才会授予权限。
Year
定义必须授予访问权限的年份。允许年份范围。此时,只有当前年份位于或等于指定的两个值之间时,才会授予权限。
Hour
定义必须授予访问权限的时间。允许指定小时范围。此时,只有当前时间介于指定的两个值之间时,才授予权限。
Minute
定义必须授予访问的分钟数。允许指定分钟的范围。此时,只有当前时间的分钟数介于指定的两个值之间,才会授予权限。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
聚合策略
如前所述,Keycloak支持构建策略的策略,称之为聚合策略。你可以使用策略聚合,通过重用现有的策略来构建更复杂策略,使得权限与策略更解耦。
要创建聚合策略,请在创建策略时选中Aggregated。
假设有一个名为_Confidential resource_的资源,该资源只能由来自keycloak.org域和特定范围的IP地址的用户访问。你当然可以同时使用这两种条件创建一个策略,但你可能希望重用此策略的域部分,以便用于其它的对来源网络无要求的场景。
这时就可以为域和网络条件创建单独的策略,并基于这两个策略的组合创建第三个策略。并将新的聚合策略应用于任意权限。
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
应用策略
定义要聚合的一个或者多个策略。这里可以选择已有的策略,也可以创建新策略。
决策逻辑
此权限的决策逻辑。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
决策逻辑
在创建聚合策略时还可以定义决策逻辑,它将用于根据各策略的结果确定最终决策。
- 一致通过
默认逻辑。意味着必须满足全部策略。
- 肯定的
要求至少一个策略是积极的,最终决策才是积极的。
- 共识的
要求正面决策的数量必须大于负面决策的数量。如果正面和负面的数量相同,那么最终的决策将是负面的。
基于客户端的策略
可以使用这种类型的策略来定义权限条件。
要创建基于客户端的策略,请在创建策略时选择Client。
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
客户端
定义此策略要授权的客户端。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
基于组的策略
此种策略允许只有你指定的组才能获取访问权限。
要创建基于组的策略,请在创建策略时选中Group。
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
组声明
在包含组名和/或路径的令牌中指定声明(即令牌key/value中的key)。通常,授权请求是基于ID Token或Access Token处理的。该策略将根据Token的组声明来获取用户所属的组。如果没有定义,则从域配置中获取。
组
在这里选择在评估策略时需要应用此策略的组。添加组之后,可以选中复选框Extend to Children来将访问权扩展到子组。如果没有标记,访问限制只适用于所选的组。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
扩展子组的访问权
默认情况下,访问限制将仅应用到配置的组内的成员。
在某些情况下,可能需要对父子结构的组统一授权。此时你可以选中Extend to Children复选框。
上面的图示的策略会对IT及其子组的成员统一授予访问权限。
积极逻辑和消极逻辑
策略可以配置为积极或消极。简单地说,可以使用这个选项来定义策略结果是应该保持原样,还是被否定。
举例来说,假设创建了策略,其中只有没有授予特定角色的用户才被授权。此时可以使用该角色创建基于角色的策略,并将其逻辑字段设置为消极(Negative)。
策略评估API
在使用JavaScript或Drools编写基于规则的策略时,Keycloak提供了评估API,它可以帮忙确定是否应该授予权限。
API提供了接口用来让开发者访问一些有用的信息,如:
- 要评估的权限,代表请求的资源和范围。
- 与请求资源所关联的属性
- 与执行上下文相关的运行时环境及其属性
- 用户相关的信息,如组以及角色
主要的接口是 org.keycloak.authorization.policy.evaluation.Evaluation,它的定义如下:
public interface Evaluation {
/**
* Returns the {@link ResourcePermission} to be evaluated.
*
* @return the permission to be evaluated
*/
ResourcePermission getPermission();
/**
* Returns the {@link EvaluationContext}. Which provides access to the whole evaluation runtime context.
*
* @return the evaluation context
*/
EvaluationContext getContext();
/**
* Returns a {@link Realm} that can be used by policies to query information.
*
* @return a {@link Realm} instance
*/
Realm getRealm();
/**
* Grants the requested permission to the caller.
*/
void grant();
/**
* Denies the requested permission.
*/
void deny();
}
在处理授权请求时,Keycloak会在评估任意策略前创建一个Evaluation实例。然后将此实例传递给每个策略,以确定访问是授予还是拒绝。
策略通过调用Evaluation实例上的 grant() 或 deny() 方法来确定是授权还是拒绝。默认情况下,Evaluation实例的状态是拒绝,这意味着策略必须显式地调用grant() 方法,以向策略评估引擎表明应该授权。
更多Evaluation API相关信息请查看这里。
评估上下文
评估上下文在评估的过程中向策略提供有用信息。
public interface EvaluationContext {
/**
* Returns the {@link Identity} that represents an entity (person or non-person) to which the permissions must be granted, or not.
*
* @return the identity to which the permissions must be granted, or not
*/
Identity getIdentity();
/**
* Returns all attributes within the current execution and runtime environment.
*
* @return the attributes within the current execution and runtime environment
*/
Attributes getAttributes();
}
策略可以从上面的接口中获取:
- 已被认证通过的身份
- 执行上下文和运行时环境的信息
身份信息基于OAuth2 Access Token来创建,创建过程会从原始Token提取所有声明。举例来说,如果你通过Protocal Mapper向OAuth2 Access Token添加了自定义的声明,你就可以在策略中获取它并利用它来构建你的授权条件。
EvaluationContext 也提供了访问执行及运行时环境的入口。但是目前只提供几个内置属性:
名称 | 描述 | 类型 |
---|---|---|
kc.time.date_time | 当前日期和时间 | String.Format MM/dd/yyyy hh:mm:ss |
kc.client.network.ip_address | 客户端的IPv4地址 | String |
kc.client.network.host | 客户端主机名称 | String |
kc.client.id | 客户端ID | String |
kc.client.user_agent | HTTP头中User-Aggent的值 | String[] |
kc.realm.name | 域名称 | String |
管理权限
权限关联了被保护的对象和要评估的策略,以决定是否应该授予访问权。
创建了资源和策略之后,我们现在来管理权限。点击Permissions选项卡:
权限主要用来管理两类对象:
- 资源
- 范围
请在创建权限的下拉列表中选择想要创建的类型。下面我们分别讲述它们的细节。
创建基于资源的权限
基于资源的权限定义了一组资源且使用一组策略来保护这些它们。
要创建基于资源的权限,请在创建权限时选中 Resource-based:
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
此权限要作用的资源类型
指定是否应用于指定类型的资源,如果选择了这个属性就需要填写资源类型。
- 资源类型
定义要保护的资源类型。Keycloak将对匹配此类型的所有资源评估此权限。
资源
此权限要保护的一组资源。
策略
定义此权限关联的一组策略。这里可以选择已有的策略或者新建。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
分类资源的权限
可以针对一组同类型的资源统一设定权限。在对有着相同访问限制的资源设置权限时非常有用。
通常可以根据项目中资源的数据或其提供的功能来分类。比如说在一个金融类的应用,其中每个银行账户属于一个特定的用户。虽然是不同的银行账户,但是可以共享全局银行机构通用的安全性要求。你可以使用分类的资源权限来定义这些通用策略,例如:
- 仅有账户拥有者才能管理自己的账户
- 仅能在账户拥有者的国家/地区进行访问
- 执行特定的身份认证方法
要创建分类资源的权限,请在创建基于资源的权限时点击Apply to Resource Type。Apply to Resource Type开启后,你就可以使用此策略来保护同一类资源了。
创建基于范围的权限
基于范围的权限定义一组范围,通过关联授权策略来保护这些范围。与基于资源的权限不同,可以使用此权限类型为与资源关联的范围创建权限,从而提供更细的权限控制粒度。
要创建新的基于范围的权限,请在新建权限时选择Scoped-based。
配置
名称
名称是一个可读的字符串。建议取一些和业务相关的名字以易于识别。
描述
此策略的详情
资源
如果定义了资源,定义的范围仅与这些资源关联的才有效。如果置空,则全部范围都有效。
范围
定义一组要保护的范围。
策略
定义此权限关联的一组策略。这里可以选择已有的策略或者新建。
逻辑
在评估完此策略相关条件之后再应用的逻辑。
策略决策逻辑
与组合策略一样,这里可以定义多个策略评估结果如何生效的最终逻辑。
- 一致通过
默认逻辑。意味着必须满足全部策略。
- 肯定的
要求至少一个策略是积极的,最终决策才是积极的。
- 共识的
积极策略的数量必须大于消极策略的数量,它们的数量相等时结果判定为消极。
评估以及测试策略
在设计策略时,你可能需要模拟测试策略的评估结果。
可以点击Evaluate选项卡来使用在线的策略评估工具。在这里你可以通过输入模拟请求来查看输出的评估结果。
输入身份信息
Identify Information过滤器用来选择发起请求的用户。
输入上下文信息
在Contextual Infomation过滤器添加额外的属性,Keycloak使用这些属性来评估策略。
输入权限
Permissions过滤器用来构建授权请求。你可以为一组资源或者范围来请求权限。如果想对全体资源或范围进行测试,点击Add后不要输入任何信息。
当全部配置好之后,点击Evaluate开始评估。