博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
9.2 角色验证——根据权限访问视图函数
阅读量:4096 次
发布时间:2019-05-25

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

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

   

   

你可能感兴趣的文章
Java 8新特性:Stream API
查看>>
管理用户状态——Cookie与Session
查看>>
最受欢迎的前端框架Bootstrap 入门
查看>>
JavaScript编程简介:DOM、AJAX与Chrome调试器
查看>>
通过Maven管理项目依赖
查看>>
通过Spring Boot三分钟创建Spring Web项目
查看>>
Spring的IoC(依赖注入)原理
查看>>
Guava快速入门
查看>>
Java编程基础:static的用法
查看>>
Java编程基础:抽象类和接口
查看>>
Java编程基础:异常处理
查看>>
Java编程基础:了解面向对象
查看>>
新一代Java模板引擎Thymeleaf
查看>>
Spring MVC中使用Thymeleaf模板引擎
查看>>
Spring Boot构建简单的微博应用
查看>>
Spring处理表单提交
查看>>
Spring MVC异常处理
查看>>
Leetcode 1180. Count Substrings with Only One Distinct Letter [Python]
查看>>
PHP 7 的五大新特性
查看>>
php使用 memcache 来存储 session
查看>>