9.2 角色验证——根据权限访问视图函数

    我们的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闭包的知识, 网上有很多教程我就不多说了, 但是只用理解闭包才会理解我下面说的,可以谈一谈写出这个闭包函数的思路:

  1.   首先我们看第二个视图函数, 修饰器的作用就相当于下面这行代码:
  2.   for_moderator_only = permission_required(Permission.MODERATE_COMMIT)(for_moderator_only)
  3.   所以for_moderator_only() = permission_required(Permission.MODERATE_COMMIT)(for_moderator_only)()
  4.   先看permission_required(Permission.MODERATE_COMMIT), 可知permission_required是最外部的函数, 参数应该是表示权限的一个变量permission,  应该返回一个内部函数——decrator(名字随便起)
  5.   然后第三点的等号右边的代码就变成了decorator(for_moderator_only)()
  6.   再看decorator(for_moderator_only), 可知内部函数decorator的参数是被修饰的函数f,然后这个decorator函数返回一个内部函数——decrated_function
  7.   第五点的代码就变成decrated_function()
  8.   最后调用这个内部函数, 根据两个外部函数提供的permissions参数和func参数,判断是否有相对应的权限, 决定返回403页面或者执行视图函数:
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, 此时我们已经可以有验证权限的函数了, 所以这个修饰器只需要借助上面的函数来实现即可:

  1. for_moderator_only = admin_required(for_moderator_only)
  2. for_moderator_only() = admin_required(for_moderator_only)()
  3. 上面我们分析过程中有一句:permission_required(Permission.MODERATE_COMMIT)(for_moderator_only)()
  4. 只要把Permission.MODERATE_COMMIT, 改成Permission.ADMINISTER不就好了吗,
  5. 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 False

login_manager.anonymous_user = AnonymousUser
  1. AnonymousUserMixin是匿名用户混合类, 本来login_manager.anonymous_user=AnonymousUserMixin,
  2. 当用户未登录时, current_user是一个匿名用户, 应该被赋值为AnonymousUserMixin类的实例, 这个类的is_authenticated()和is_active()方法都返回False;
  3. 此处我们自己实现了匿名用户类, 并把login_manager.anonymous_user赋值为AnonymousUser,
  4. 这样用户未登录时current_user就被赋值为AnonymousUser的实例, 这样用户不登录也可以调用can和is_admin

举例说明:

完成以上三步工作后, 如果我们访问/moderator   url, 因为@login_required, 程序会要求我们先登录, 登录以后, 由于auth.login视图函数的重定向, return redirect(requests.args.get('next')), 程序会重新发送/moderator的请求, 然后@permission_required的作用, 我们会逐级访问闭包函数,判断是该返回403还是执行视图函数。

为什么要使用闭包?

  1. 为什么要使用闭包, 因为如果把判断条件放在视图函数里面显然是不合适的, 我们目的就是阻止无权限用户访问视图函数;
  2. 使用闭包可以减少代码的重复量, 否则我们每个视图函数都要添加同样你的代码。

四. 把Permission添加到模板的上下文, |-app/-main/__init__.py

from flask import Blueprint

from ..models import Permission

main = 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, 所以执行视图函数返回页面。

   









   


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值