目录
- 官方地址:https://pythonhosted.org/Flask-Principal/
- 学习的步骤: 先学会如何使用,再学会深层原理,而不是上来直接分析原理,因为你分析不明白。
- 该框架干啥的:权限控制
- 安装:pip install flask-principal
一: Flask-Principal的使用
1.1: 管理员用户才能访问:
-
定义一个权限,然后在需要进行权限控制的地方使用装饰器进行装饰就可以了。
-
如果是一个视图中的部分代码逻辑,需要权限才能执行,那么使用上下文进行管理。
from flask import Flask , Response from flask.ext.principal import Principal , Permission , RoleNeed app = Flask ( __name__ ) # 加载扩展 principals = Principal ( app ) # 创建具有单个需求的权限,在本例中为 RoleNeed。 admin_permission = Permission ( RoleNeed ( 'admin' )) # 使用需要的主体保护视图 @app.route ( '/admin' ) @admin_permission.require () def do_admin_index (): return Response ( 'Only if you are an admin' ) # 这次用上下文管理器保护 @app.route ( '/articles' ) def do_articles (): with admin_permission.require (): return Response ( 'Only if you are admin' )
1.2: 登录退出时身份转换:
-
当用户登录的时候和退出登录的时候,用户是需要进行身份转换的。那么如何处理呢?
-
用户登录的时候,使用send函数发送消息给当前项目,表示身份发生了转变。
@app.route('/login', methods=['GET', 'POST']) def login(): # A hypothetical login form that uses Flask-WTF form = LoginForm() # Validate form input if form.validate_on_submit(): # Retrieve the user from the hypothetical datastore user = datastore.find_user(email=form.email.data) # Compare passwords (use password hashing production) if form.password.data == user.password: # Keep the user info in the session using Flask-Login login_user(user) # 身份发生改变,向程序发送 identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return redirect(request.args.get('next') or '/') return render_template('login.html', form=form)
-
当用户退出登录的时候,将身份转变成匿名用户:
@app.route('/logout') @login_required def logout(): # Remove the user information from the session logout_user() # Remove session keys set by Flask-Principal for key in ('identity.name', 'identity.auth_type'): session.pop(key, None) # 身份进行转变,变成匿名用户。 identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity()) return redirect(request.args.get('next') or '/')
1.3: 权限加载器:
-
整个项目中必须有一个给当前的用户增加权限的函数,而这个函数我们成为权限加载器。
-
函数名一般来说固定: on_identity_loaded,这个函数必须使用@identity_loaded.connect_via(app)装饰器装饰。
from flask.ext.login import current_user from flask.ext.principal import identity_loaded, RoleNeed, UserNeed @identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): # 1:首先拿到当前的用户对象 identity.user = current_user # 2:增加当前用户拥有的权限 if hasattr(current_user, 'id'): identity.provides.add(UserNeed(current_user.id)) # 3:如果user中存在一个字段roles表示该用户还拥有的角色(一对多),那么我们可以循环遍历这个角色,将角色加入到身份中去,这样用户就拥有了多种权限。 if hasattr(current_user, 'roles'): for role in current_user.roles: identity.provides.add(RoleNeed(role.name))
1.4: 资源保护:
-
需求: 假如时一个博客系统,现在要求,只有作者才能编辑博客。
-
如何实现?
- 1: 首先,从表的存储上来讲,user表必须对应着一个posts表(posts权限表), 并且是一对多的关系。(一个user有多个post权限)
- 2: 当用户登录的时候,我们根据user,拿到所有的post,将这些post加入到当前用户的权限中。(用户有了这些权限)
- 3: 在进行编辑的时候,前端发送需要传递一个post_id,根据这个post_id构造一个权限。调用permission.can()执行看看这个权限当前用户是否存在,如果存在则进行视图中的逻辑处理。
-
具体实现:
-
1: 构造生成权限的类:
from collections import namedtuple from functools import partial from flask.ext.login import current_user from flask.ext.principal import identity_loaded, Permission, RoleNeed, \ UserNeed BlogPostNeed = namedtuple('blog_post', ['method', 'value']) EditBlogPostNeed = partial(BlogPostNeed, 'edit') class EditBlogPostPermission(Permission): def __init__(self, post_id): need = EditBlogPostNeed(unicode(post_id)) super(EditBlogPostPermission, self).__init__(need)
-
2: 登录的时候,给当前用户赋予权限:
@identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): ... # Assuming the User model has a list of posts the user # has authored, add the needs to the identity if hasattr(current_user, 'posts'): for post in current_user.posts: identity.provides.add(EditBlogPostNeed(unicode(post.id)))
-
3: 视图逻辑校验的时候,看前端视图需要的权限,该用户是否存在,存在,执行逻辑处理,不存在则抛出403权限异常。
@app.route('/posts/<post_id>', methods=['PUT', 'PATCH']) def edit_post(post_id): permission = EditBlogPostPermission(post_id) if permission.can(): return render_template('edit_post.html') abort(403) # HTTP Forbidden
-
二: 原理分析:
2.1: flask_principal四大主件:
- 1: Identity(身份): 一个用户对应一个身份,一个身份有众多的权限。
- 2: Permission(权限): 满足某种需求的能力。
- 3: Needs(访问控制单元) : 分为两类: 角色需求(管理员,超级管理员),动作需求(可以编辑,可以上传图片)
- 4: IdentityContext(权限上下文) : 有关权限的装饰器(权限处理)和上下文管理器(权限控制)。
"""
白话讲解:
1:身份是个啥? 来一个人,我就给你发个牌子,代表你的身份。
2:权限是个啥? 一个是表示你职位(职位能做的都能做),一个代表你还能干啥(例如:领导单独给你的权力)。
3:访问控制单元是个啥?职位对应能做的事,单独权力能做的事。
4:权限上下文是个啥?你这个身份咋就有了权限(装饰器控制),咋判断有没有权限(装饰器或者上下文管理器),这些权限上下文处理了,作为码农的我们就不要考虑了。
"""
- 官方图解: