Flask框架中的综合认证方案
1.统一处理
- 需求: 获取用户身份
- 分析: 除了静态资源, 基本所有视图都需要获取用户身份, 每个视图单独获取出现大量的代码冗余
- 解决办法: 设置 请求钩子, 并通过 g变量 将数据传递给视图函数
代码示例:
from flask import Flask, session, g
app = Flask(__name__)
app.secret_key = 'test'
# 需求1: 所有视图都需要获取用户身份
# 解决办法: 用钩子函数进行封装 减少代码冗余
@app.before_request
def prepare():
# 必须使用g变量来传递数据, 使用全局变量不能记录并发的多个请求数据
g.name = session.get('username')
@app.route('/')
def index():
if g.name:
return "欢迎回来, %s" % g.name
else:
return '首页'
@app.route('/demo1/')
def demo1():
print(g.name)
return 'demo1'
@app.route('/login')
def login():
"""登录"""
session['username'] = 'zs'
return '登录成功'
if __name__ == '__main__':
app.run(debug=True)
2.访问限制
- 需求: 对指定的路由进行访问限制
- 分析: 部分视图需要身份校验, 这部分视图每个单独校验仍会出现大量的代码冗余
- 解决办法: 封装 装饰器 完成身份校验逻辑, 对指定视图函数设置装饰器
代码示例:
from flask import Flask, session, g, abort
from functools import wraps
app = Flask(__name__)
app.secret_key = 'test'
@app.before_request
def prepare():
g.name = session.get('username')
@app.route('/')
def index():
if g.name:
return "欢迎回来, %s" % g.name
else:
return '首页'
@app.route('/login')
def login():
"""登录"""
session['username'] = 'zs'
return '登录成功'
# 需求2: 对部分视图进行访问限制 如个人中心必须登录才能访问
# 解决方案: 使用装饰器封装访问限制 减少代码冗余
def login_required(f): # f = user
def wrapper(*args, **kwargs):
# 获取函数名
print(wrapper.__name__)
if g.name: # 用户已登录
return f(*args, **kwargs) # 正常访问视图函数
else: # 用户未登录
abort(401) # 400 语法/参数错误 401 未认证 403 已认证, 权限不足 404 资源不存在 405 请求方式不支持 500 服务器错误
return wrapper
@app.route('/user')
@login_required # user = login_required(user)
def user():
"""个人中心"""
return '访问 %s 的个人中心' % g.name
if __name__ == '__main__':
print(app.url_map)
app.run(debug=True)
补充:
functools.wraps
- 系统内置的装饰器, 主要用于装饰器中的闭包函数
- 作用是 将被装饰的函数(wrapper)的函数信息 替换为 指定函数(f)的函数信息 (包括name 函数名, doc 函数注释等)
flask中的函数标记是根据函数名生成的
视图函数添加装饰器login_required 后, 函数标记都会使用闭包函数名wrapper, 这样会出现函数标记冲突, 程序报错
解决办法: 给闭包函数添加装饰器functools.wraps(f), 让函数标记使用原视图函数名生成
代码示例:
# 使用装饰器封装访问限制
def login_required(f): # f = user
@wraps(f) # 会将被装饰的函数(wrapper)的函数信息替换为指定函数(f)的函数信息(__name__ 函数名, __doc__ 函数注释)
# 设置该装饰器后, 可以让闭包函数使用原函数名, 避免函数标记出现冲突(函数标记是根据函数名来生成的)
def wrapper(*args, **kwargs):
if g.name:
return f(*args, **kwargs)
else:
abort(401)
return wrapper