本文共 3872 字,大约阅读时间需要 12 分钟。
我们的web程序用户有不同的角色——普通用户, 管理评论的人, 权限最高的管理员。 不同的权限可以浏览不同的页面, 权限越大, 可以浏览的页面就越多。
本节我们为试图函数添加权限限制, 使其只可以被管理员或评论管理员访问。
————————————————————————————————————
一. 修改|-app/-main/views.py
@main.route('/admin')@login_required@admin_required #只有管理员能访问该视图函数def for_admin_only(): return "For administrator!"@main.route('/moderator')@login_required@permission_required(Permission.MODERATE_COMMIT) #只有评论员能访问该视图函数def for_moderator_only(): return "For moderator!"
上图的两个修饰器是很简单明了的, 如果flask为我们提供了这样的修饰器, 就会很方便, 不过现实是flask并没有为我们封装这个功能, 需要我们自己来实现;
二.增加|-app/decorators.py
这就需要python闭包的知识, 网上有很多教程我就不多说了, 但是只用理解闭包才会理解我下面说的,可以谈一谈写出这个闭包函数的思路:
def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): #该函数由我们自己实现, 如果用户有该权限, 返回True, 见标题三 abort(403) return f(*args, **kwargs) return decorated_function return decorator
再来看第一个视图函数, 修饰器@admin_required, 此时我们已经可以有验证权限的函数了, 所以这个修饰器只需要借助上面的函数来实现即可:
- for_moderator_only = admin_required(for_moderator_only)
- for_moderator_only() = admin_required(for_moderator_only)()
- 上面我们分析过程中有一句:permission_required(Permission.MODERATE_COMMIT)(for_moderator_only)()
- 只要把Permission.MODERATE_COMMIT, 改成Permission.ADMINISTER不就好了吗,
- permission_required(Permission.ADMINISTER)(for_moderator_only)()
所以admin_required代码如下:
def admin_required(f): return permission_required(Permission.ADMINISTER)(f)
三. 修改|-app/models.py
class User(UserMixin, db.Model): def can(self, permissions): #判断用户是否有该权限 return self.role is not None and (self.role.permissions & permissions) == permissions def is_admin(self): #判断用户是否是管理员 return self.can(Permission.ADMINISTER)class AnonymousUser(AnonymousUserMixin): 匿名用户无任何权限 def can(self, permissions): return False def is_admin(self): return Falselogin_manager.anonymous_user = AnonymousUser
举例说明:
完成以上三步工作后, 如果我们访问/moderator url, 因为@login_required, 程序会要求我们先登录, 登录以后, 由于auth.login视图函数的重定向, return redirect(requests.args.get('next')), 程序会重新发送/moderator的请求, 然后@permission_required的作用, 我们会逐级访问闭包函数,判断是该返回403还是执行视图函数。
为什么要使用闭包?
四. 把Permission添加到模板的上下文, |-app/-main/__init__.py
from flask import Blueprintfrom ..models import Permissionmain = Blueprint('main', __name__) #定义蓝本@main.app_context_processor #添加上下文, 这样在模板中就可以使用Permission类了def inject_permissions(): return dict(Permission=Permission)from . import views, errors
五. 过程演示
我们用用户leo来说明访问管理页面/admin的过程
一开始leo用户没有角色(role_id为NULL):
我们在浏览器访问管理页面, 因为修饰器@login_required的原因, 重定向到登录页面:
输入leo的email和密码后, 点击提交按钮, 用户登录后, auth.login函数会重定向到main.for_admin_only, return redirect(url_for(request.args.get('next')))(ps: 重定向到上次未授权的页面url), 然后调用视图函数, for_admin_only(), 根据我们上面的分析, 此时用户没有角色, 返回403页面:
我们为leo添加管理员角色:
添加成功:
然后启动服务器, 再次尝试访问/admin页面:
这次因为leo有角色, 且权限为Permission.ADIMNISTER, 所以执行视图函数返回页面。