用户授权(权限管理)

未匹配的标注

Masonite 也提供一种简单的方式授权用户对资源操作。基于两个概念:GatesPoliciesGates 顾名思义就是一种授权安检,在操作资源前需要校验用户是否有权访问。Policies 是一种围绕模型进行分组授权逻辑方式。

门(Gates)

Gates 是简单的可调用对象,它将定义用户是否被授权执行给定的操作。可使用方便的 Gate 门面来轻松操纵 Gates

注册门

门将用户实例作为第一个参数,并可能接收额外的参数,例如模型实例。你需要在服务提供者的 boot() 方法中定义 Gates

在下面的示例中,我们正在添加门以验证用户是否可以创建和更新帖子。如果用户是管理员,则可以创建帖子,并且只能更新他们自己创建的帖子。

from masonite.facades import Gate

class MyAppProvider(Provider):

    def boot(self):
        Gate.define("create-post", lambda user: user.is_admin)
        Gate.define("update-post", lambda user, post: post.user_id == user.id)

你可以使用 has() 来检查门是否存在或已注册,它将返回一个布尔值:

Gate.has("create-post")

提示:如果使用未知的门,则会引发 GateDoesNotExist 异常。

授权操作

然后,在代码的任何位置(通常在控制器中),你可以使用这些门来检查 当前已认证用户 是否被授权执行门所定义的给定操作。

Gates 使用不同的方法来执行验证:allows()denies()none()any()authorize()inspect()

allows()denies()none()any() 返回一个布尔值,表示用户是否被授权。

if not Gate.allows("create-post"):
    return response.redirect("/")

# ...
post = Post.find(request.input("id"))
if Gate.denies("update-post", post):
    return response.redirect("/")

# ...
Gate.any(["delete-post", "update-post"], post)

# ...
Gate.none(["force-delete-post", "restore-post"], post)

authorize() 不返回布尔值,而是会引发一个 AuthorizationException 异常,该异常将呈现为带有 403 状态码的 HTTP 响应。

Gate.authorize("update-post", post)
# 如果程序执行到这里,那么用户已被授权。
# 否则,将会抛出一个异常,并返回一个状态码为 403 的错误信息:"Action not authorized"。

最后,为了更好地控制授权检查,你可以使用 inspect() 分析响应:

response = Gate.inspect("update-post", post)
if response.allowed():
     # 做一些事情
else:
     # 没有授权,但我们可以访问消息。
     Session.flash("errors", response.message())

通过用户模型

可以将 Authorizes 类添加到用户模型中,以允许快速权限检查:

from masonite.authentication import Authenticates
from masonite.authorization import Authorizes

class User(Model, Authenticates, Authorizes):
    #..

现在,User 实例上将提供流畅的授权 API:

user.can("delete-post", post)
user.cannot("access-admin")
user.can_any(["delete-post", "force-delete-post"], post)

所有这些方法都接收门名称作为第一个参数,然后根据需要添加一些其他参数。

使用给定的用户

你可以在 Gate 门面上使用 for_user() 方法来针对给定的用户进行验证,而不是针对已认证用户。

from masonite.facades import Gate

user = User.find(1)
Gate.for_user(user).allows("create-post")

门钩子

在门授权过程中,可以触发 beforeafter 钩子。

可以按以下方式添加 before 钩子:

# 在这里,管理员用户将始终基于此响应的布尔值进行授权。
Gate.before(lambda user, permission : user.role == "admin")

after 钩子的工作方式相同:

Gate.after(lambda user, permission, result : user.role == "admin")

如果 after 回调返回一个值,它将优先于门结果检查。

策略(Policies)

策略是围绕特定模型组织授权逻辑的类。

创建策略

可以运行以下命令:

$ python craft policy AccessAdmin

也可以使用 --model 标志创建一个具有一组预定义门的策略:

$ python craft policy Post --model

模型策略带有我们可以在模型上执行的常见操作:

from masonite.authorization import Policy


class PostPolicy(Policy):
    def create(self, user):
        return False

    def view_any(self, user):
        return False

    def view(self, user, instance):
        return False

    def update(self, user, instance):
        return False

    def delete(self, user, instance):
        return False

    def force_delete(self, user, instance):
        return False

    def restore(self, user, instance):
        return False

你可以自由添加任何其他方法到你的策略中:

from masonite.authorization import Policy


class PostPolicy(Policy):
    #.. 

    def publish(self, user):
        return user.email == "admin@masonite.com"

注册策略

然后在你的服务提供者中(如定义门一样),你应该注册策略并将其绑定到一个模型:

from masonite.facades import Gate
from app.models.Post import Post
from app.models.User import User

from app.policies.PostPolicy import PostPolicy
from app.policies.UserPolicy import UserPolicy

class MyAppProvider(Provider):

    #..

    def register(self):
        Gate.register_policies(
            [(Post, PostPolicy), (User, UserPolicy)],
        )

对于 Post 模型的示例策略可能如下所示:

from masonite.authorization import Policy

class PostPolicy(Policy):
    def create(self, user):
        return user.email == "idmann509@gmail.com"

    def view(self, user, instance):
        return True

    def update(self, user, instance):
        return user.id == instance.user_id

提示:如果使用了未知的策略,则会引发 PolicyDoesNotExist异常。

授权操作

然后,你可以使用 Gate 门面方法来授权在你的策略中定义的操作。使用先前定义的PostPolicy ,我们可以进行以下调用:

from masonite.facades import Gate

post = Post.find(1)
Gate.allows("update", post)
Gate.denies("view", post)
Gate.allows("force_delete", post)

create()view_any() 方法不需要模型实例,因此应该提供模型类,以便 Gate 机制可以推断出这些方法属于哪个策略。

Gate.allows("create", Post)
Gate.allows("view_any", Post)

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/masonite/4.0/fe...

译文地址:https://learnku.com/docs/masonite/4.0/fe...

上一篇 下一篇
贡献者:4
讨论数量: 0
发起讨论 查看所有版本


暂无话题~