这是我参与更文挑战的第12天,活动详情查看: 更文挑战
业务场景
首先, 权限控制有两种方式: RBAC和ACL
RBAC是基于角色的控制方式, 而ACL中权限直接与用户相关
Flask在笔者理解的初衷是快准狠, 所以我们放弃了较为复杂的ACL, 使用RBAC进行权限管理
根据RBAC的理念, 简单讲述下系统的权限构建:
- 使用接口/权限/角色/用户四个概念来进行控制, 并创建对应的表
- 创建接口与权限、权限与角色, 以此类推的关系表
- 用户登陆时查询所拥有的接口信息
- 访问接口时进行校验
其中的前三步网上已经有非常多的案例了, 代码也十分简单(除了树形关系处理)
本文着重讲一下第四步, 利用Python的装饰器实现接口校验
方案
问题思路
在设计权限校验时, 第一反应是根据请求的url进行匹配
例如接口为/api/user, 则在用户的接口信息中进行匹配, 如果不存在则没有对应权限
但对于endpoint格式的接口, 这里就出现难题了, /api/user/1怎么处理呢?
很多小伙伴肯定能想到正则, 但这样还需要为不同的接口定制规则, 且资源id的不同甚至会影响到接口的设计, 不具备适用性
所以, 正确的做法应该是使用代码, 为接口确定代码并进行校验
编写装饰器
明确了解决方案后, 我们开始编写代码
第一步, 安装flask_jwt_extended, 为什么使用token就不需要再说明了吧
第二步, 编写一个带有接口代码的装饰器:
from functools import wraps
from flask_jwt_extended import verify_jwt_in_request, get_jwt
from flask_koala.core.exceptions import BusinessException
def authorization(code: str = None):
"""
鉴权装饰器
"""
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
try:
verify_jwt_in_request()
except Exception:
raise BusinessException("无访问权限", 403)
if code is None:
return fn(*args, **kwargs)
authorities = get_jwt()['authorities']
if code not in authorities:
raise BusinessException("无访问权限", 403)
return fn(*args, **kwargs)
return wrapper
return decorator
这里我使用了全局自定义异常, 把BusinessException换成你需要的即可
最后一步, 只需要在接口上加上注解即可:
@auth.route('/info')
class Info(Resource):
@authorization(code='api:auth:info')
def get(self):
"""
获取用户信息
:return: 用户信息实体
"""
return RestResult.success(get_jwt_identity())