用户授权(权限管理)
Masonite 也提供一种简单的方式授权用户对资源操作。基于两个概念:Gates
和 Policies
。Gates
顾名思义就是一种授权安检,在操作资源前需要校验用户是否有权访问。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")
门钩子
在门授权过程中,可以触发 before
和 after
钩子。
可以按以下方式添加 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)
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。