asseco-voice / laravel-json-authorization
Laravel JSON 授权
Requires
- php: ^8.1
- ext-json: *
- asseco-voice/laravel-common: ^3.0
- asseco-voice/laravel-json-query-builder: ^2.0
- laravel/framework: ^10.0
Requires (Dev)
- fakerphp/faker: ^1.9.1
- mockery/mockery: ^1.4.4
- orchestra/testbench: ^8.5
- phpunit/phpunit: ^10.0
README
Laravel JSON 授权
此包通过在每个可授权模型上施加 JSON 对象来实现授权。
该包主要针对多个 Laravel 微服务授权进行开发,目的是避免额外的授权服务访问。
这也使得非授权服务成为自包含的。认证服务应提供角色(或任何其他形式的授权),而服务应提供施加在任何角色上的限制。如果需要替换认证服务,唯一的责任是重新映射新认证服务上的角色,而角色限制将保持不变。
为什么采用这种方法?
此包为对 Eloquent 模型施加权利提供了很大的灵活性。使包独特的是,它将开关的概念切换到了一个你不想保护路由,而是 保护资源本身 的方式。
这反过来又带来了两个很好的好处,这是路由方法没有的
- 调用单个端点并不意味着它操作单个模型,这使得路由方法不可能对你想保持保护的东西进行底层保护。
- 调用受保护的模型上的关系不会保护相关模型,所以如果你通过 Eloquent 关系 eager/lazy 加载某些内容,你无法保护正在解析的内容。
在此处施加的资源保护对您提供的限制施加了限制,而不管您的请求来自何处。我们是通过利用 Laravel 范围和 Eloquent 事件来做到这一点的。
当然,也有一些限制
- 如果手动将
relation_id
转发到模型,则关系将不会受到保护,例如,如果ContactType
有许多Contacts
,则ContactType
只能更新具有 contact type ID 1 的联系人,以下将仍然通过作为有效:Contacts::create([... 'contact_type_id' = 2 ...])
- 包将尝试根据提供的限制提前授权,但对于复杂限制,包将对数据库进行选择,这在某些情况下可能是一项沉重的操作。这主要影响创建/更新/删除权利,而不是读取权利。
安装
通过 composer 安装此包。它将自动注册为 Laravel 服务提供者,因此无需采取其他措施来注册包。
composer require asseco-voice/laravel-json-authorization
术语
- 调用某物 可授权 意味着它可以被授权
- 可授权集 - 一组可授权用户属性(例如,一个 角色 集合被视为一个 可授权集)
- 可授权集值 - 可授权集 中的一个单独对象(例如,单个 角色 -
example_role_1
) - 可授权集类型 - 逻辑 可授权集 分离(例如,你可以有一个 角色 集合,一个 组 集合...,这将被分类为 可授权集类型)
- 可授权模型 - 可在之上实施授权的模型
- 权利 - 单个 CRUD 权利,用于单个 可授权模型 和单个 可授权集值(例如,为某些模型拥有 创建权利)
- 规则 - 单个 可授权模型 和单个 可授权集值 的 权利 集合
用法
包初始化需要几个步骤来设置
选择可授权模型
需要保护的模型必须实现 Asseco\JsonAuthorization\App\Traits\Authorizable
特性。
完成后,务必运行 php artisan asseco:sync-authorizable-models
以同步实现了 Authorizable
特性的模型与数据库。
每次添加或删除模型中的 Authorizable
特性时,都要运行此命令。
如果模型已与其他规则存在关联,则命令将抛出异常。这是故意为之,以便您手动删除即将删除的模型的规则,以免意外发生。
迁移表
运行 php artisan migrate
将发布 3 个表
authorization_rules ----M:1--- authorizable_models
|
|
M
-
1
|
|
authorizable_set_types
authorizable_models
- 包含 可授权模型 的完整 Eloquent(命名空间)模型列表。在包使用时自动填充此表,但一旦写入数据库,即使删除了特性也不会自动删除。仅扫描 app
文件夹内的模型。如果您的文件夹结构不同,或需要实现外部模型,请修改配置中的 models_path
变量以包含您所需的。
authorization_rules
- 包含 可授权集合值 和对它们施加的 规则 的列表。
authorizable_set_types
- 类型代表不同的授权集合。如果您仅按角色进行授权,则其中只应有 roles
,但在某些情况下,您可能希望合并来自不同 可授权集合类型 的 可授权集合值,在这种情况下,您也会添加这些。
关于性能,几乎所有内容都进行了缓存,并在更改时失效和重新缓存。
可以使用包含在您的应用 DatabaseSeeder
中的 AuthorizationSeeder
(多个播种器的包装器)来使用播种器。如果需要,您也可以包含该类中的单个播种器。
修改用户
用户应实现 AuthorizesUsers
接口,该接口要求您实现一个方法。
该方法应返回当前认证用户的 可授权集合及其值 的数组。
这需要反映来自 authorizable_set_types
表的名称作为数组键,以及为每个 可授权集合类型 设置的 可授权集合值。
示例
authorizable_set_types
ID Name
1 roles
2 groups
3 id
public function getAuthorizableSets(): array
{
return [
'roles' => Auth::user()->roles,
'groups' => Auth::user()->groups,
'id' => Auth::user()->id,
];
}
虽然不需要实现所有这些,但以下也是有效的(只要 roles
位于 authorizable_set_types
表中)
public function getAuthorizableSets(): array
{
return [
'roles' => Auth::user()->roles
];
}
根据集合来源,您可以提供任何返回授权对象数组的方法
public function getAuthorizableSets(): array
{
return [
'roles' => $someClass->methodCall(Auth::user()->id, 'https://my-external-service')
];
}
一旦解析完毕,函数应返回例如
return [
'roles' => ['role1', 'role2'...],
'groups' => ['group1', 'group2'...],
...
]
值得注意的是,最终产品是角色规则的合并。
示例
role 1: "read" right for IDs 1, 2 and 3
role 2: "read" right for IDs 4, 5 and 6
Final "read" right for that user are IDs 1, 2, 3, 4, 5 and 6
附加规则
如果一个模型是 可授权的,并且 authorization_rules
表中没有当前登录用户的限制,则我们假设该用户没有操作该模型的权利。您有责任明确指出谁有权做什么。
可能的权利有
- 创建
- 读取
- 更新
- 删除
每个 可授权集合值 都将有一个针对单个模型的规则集(以 JSON 格式)。
此包是在 JSON 查询构建器 的基础上构建的,您可以在其中深入了解查询逻辑,并添加绝对权利 *
。
要使用绝对权限,您可以这样做:
{
"read": "*"
}
为给定模型的所有行赋予读取权限。
如果您需要某种形式的具有对一切绝对权限的管理员,请发布配置并将其添加到absolute_rights
键,这样您就不需要为它明确提供CRUD权限。
虚拟角色
如果您需要在全局范围内保护资源或给系统中的所有用户赋予单个资源的权限,您可以这样做,方法是利用虚拟角色。默认情况下,该角色是voice-all-mighty
,但可以使用.env
中的VIRTUAL_ROLE
值来覆盖。
虚拟角色绝对不能作为您的认证服务中的一个标准角色存在。这将与之冲突并且不会正常工作。
这将以这种方式工作,例如,为某些资源赋予对虚拟角色的读取权限,然后所有其他用户都会继承这些权限。
示例
ID Role Authorization model ID
1 voice-all-mighty 1
Rules
{
"read": {
"search": {
"id": "=1"
}
}
}
将为系统中的所有用户独立于他们的系统角色赋予对模型1
的读取权限。
刷新缓存
由于此包必须执行繁重的工作,因此所有内容都会使用1天的TTL进行缓存。请确保在每次手动代码更新(例如,您在一个模型上添加了Asseco\JsonAuthorization\App\Traits\Authorizable
特质)后清除缓存。
您可以通过Laravel方式清除缓存,或者如果您使用Redis作为缓存驱动程序,您可以使用我们的一个包来启用通配符Redis清除。
示例
假设我们有以下受保护的模型:
authorizable_models
ID Name
1 App\Contact
让我们为名为agent
的角色设定权限
authorization_rules
ID Role Authorization model ID
1 agent 1
Rules
{
"create": "*",
"read": {
"search": {
"id": "=1;2;3;4;5"
}
},
"update": {
"search": {
"id": "=!2"
}
}
}
这些权限可以大致翻译如下:
- 您可以无限制地创建联系人
- 您只能读取ID为1、2、3、4和5的联系人。这意味着调用
Contact::all()
将仅返回5条记录。此外,调用Contact::find(6)
将不会返回记录。 - 您可以更新ID不等于2的任何联系人。重要的是要说明,读取权限是一个顶级权限,它将初始限制可能的输出为1、2、3、4和5,实际上意味着您可以更新ID为1、3、4和5。其他权限由于强制的读取权限而被禁止。
- 由于省略了删除选项,您没有删除任何联系人的权限
扩展包及其他
发布配置将使您能够更改包模型以及控制迁移的行为。如果扩展模型,请确保您正在扩展您的实现中的原始模型。
为了开发目的,您可以通过将以下内容添加到您的.env
文件中完全禁用授权:
OVERRIDE_AUTHORIZATION=true