【摘要】
上一章节,我们主要对Web应用的用户认证做了详细的讲解,包括使用Flask实现用户注册,登录,并通过Session机制实现用户保持登录。那么在了解了用户认证之后,这一章节我们就着重介绍一下权限校验的原理以及实现方式。
【为什么需要鉴权】
用户在通过认证之后,已经可以正常访问我们的后端应用,但当后端应用越来越完善,功能越来越丰富,并且牵扯的资源以及用户的范围都足够广的时候,用户的权限校验就显得尤为重要。例如:
1.是否所有用户都可以通过接口获取CMDB的数据信息;
2.是否所有的用户都可以调用接口对设备进行操作;
3.是否拥有获取CMDB信息权限的用户也拥有对设备进行操作的权限;
4.是否拥有对设备操作权限的用户,就可以对全部的设备进行操作;
诸如上述的权限问题还有很多,下面就一起来看看如何在Flask应用中进行鉴权。
【Flask实现】
对用户鉴权相信大家都可以理解,其实就是判断用户是否有权限访问某个API,在实现上也相对比较简单。
上一章节中,我们通过装饰器实现了登录认证,伪代码如下:
def permission(func):
@wraps(func)
def inner():
if not auth():
return Fail
return func()
return inner
@app.route("/index")
@permission
def index():
return "success"
登录认证的具体实现其实就是将该装饰器加在需要认证的路由函数上,这样就可以在调用该路由函数前进行一系列的认证过程。
基于用户鉴权
如果想要在登录认证的前提下再进行用户鉴权,则只需要修改permission装饰器,使其可以对用户进行判断,然后决定是否允许该用户访问。但是我们需要先明确具体的路由函数允许哪个用户访问,并且将这个限制传入装饰器中,这时候就需要用到【自动化运维新手村】装饰器-进阶中的带参数的装饰器,伪代码如下:
def permission(permit_users):
def login_acquired(func):
@wraps(func)
def inner():
if not auth() or current_user not in permit_users:
return Fail
return func()
return inner
return login_acquired
@app.route("/index")
@permission(["ethan", "john", "jack"])
def index():
return "success"
经过改进的装饰器加在需要鉴权的路由函数上,并且传入该路由函数允许访问的用户列表,这样在登录认证时,通过session_id获取到当前用户,判断该用户是否在允许访问的用户列表中即可。
其实不难看出,通过用户去区分权限显然是不太现实的,用户数量增多的时候,可能会让权限控制变得十分难以维护。
那么最先想到的改进方法,应该就是将不同的用户赋予不同的角色,这样在权限控制的时候,鉴权粒度就由用户变成了角色。
基于角色鉴权
关于通过角色鉴权目前业内已经有一套成熟的规范 —— RBAC(Role-Based Access Control, 基于角色的访问控制),就是用户通过角色与权限进行关联。
RBCA本质上是对用户进行分组管理,赋予角色,对权限进行合理的划分,最终实现一个用户拥有若干角色,每一个角色拥有若干权限。并且RBCA具有十分完善的权限模型设计,对于大型系统的权限管理是非常重要的,但这一章节目的就是化繁为简,学会其权限管理的基本原理和底层实现。
定义角色
这里定义角色时引入了一个新的概念,叫做枚举类型,枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、状态等,那么我们这里的角色显然也适合用枚举类型来定义。
from enum import Enum
class Role(Enum):
ADMIN = "admin"
CMDB = "cmdb"
GUEST = "guest"
由于到目前为止,我们的后端应用还没有引入数据库的概念,所以权限信息可以和用户信息一起暂时保存在JSON文件中,这里用户的权限信息可以通过在用户信息中新增一个role字段来进行标识。模型如下:
[
{
"username": "",
"password": "",
"role&