Flask-restful API 授权。在装饰器中访问 current_identity
Posted
技术标签:
【中文标题】Flask-restful API 授权。在装饰器中访问 current_identity【英文标题】:Flask-restful API Authorization. Access current_identity inside decorator 【发布时间】:2016-03-17 00:33:41 【问题描述】:我使用 flask-restful 来创建我的 API。我使用flask-jwt
启用基于JWT
的身份验证。现在我需要做授权。
我尝试过放置我的授权装饰器。
test.py (/test api)
from flask_restful import Resource
from flask_jwt import jwt_required
from authorization_helper import authorized_api_user_type
class Test(Resource):
decorators = [jwt_required(), authorized_api_user_type()]
def get(self):
return 'GET OK'
def post(self):
return 'POST OK'
基本上要处理基本授权,我需要访问current_identity
并检查它的类型。然后根据它的类型,我将决定用户是否有权访问 api / 资源。
但current_identity
在那个装饰器中似乎是empty
。因此,为了间接获得它,我必须查看jwt_handler
的代码并在那里完成操作。
authorization_helper.py
from functools import wraps
from flask_jwt import _jwt, JWTError
import jwt
from models import Teacher, Student
def authorized_api_user_type(realm=None, user_type='teacher'):
def wrapper(fn):
@wraps(fn)
def decorator(*args, **kwargs):
token = _jwt.request_callback()
if token is None:
raise JWTError('Authorization Required', 'Request does not contain an access token',
headers='WWW-Authenticate': 'JWT realm="%s"' % realm)
try:
payload = _jwt.jwt_decode_callback(token)
except jwt.InvalidTokenError as e:
raise JWTError('Invalid token', str(e))
identity = _jwt.identity_callback(payload)
if user_type == 'student' and isinstance(identity, Student):
return fn(*args, **kwargs)
elif user_type == 'teacher' and isinstance(identity, Teacher):
return fn(*args, **kwargs)
# NOTE - By default JWTError throws 401. We needed 404. Hence status_code=404
raise JWTError('Unauthorized',
'You are unauthorized to request the api or access the resource',
status_code=404)
return decorator
return wrapper
为什么我不能只在我的 authorized_api_user_type
装饰器中访问 current_identity
?在 flask-restful 中进行授权的正确方法是什么?
【问题讨论】:
【参考方案1】:我目前的解决方案如下:
@app.before_request
def detect_something():
header = request.headers.get('Authorization')
if header:
_, token = header.split()
request.identity = identity(jwt.decode(token,
app.config['SECRET_KEY']))
之后,我们可以通过request.identity
访问装饰器中的身份。我从代码中到处删除了current_identity
。它仍然是混乱的方式。
【讨论】:
什么是乱七八糟的方式?你建议什么?【参考方案2】:这里是Flask-JWT
和Flask-Restful
的快速入门组合。
from flask import Flask
from flask_restful import Resource, Api, abort
from functools import wraps
app = Flask(__name__)
api = Api(app)
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp
class User(object):
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
def __str__(self):
return "User(id='%s')" % self.id
users = [
User(1, 'user1', 'abcxyz'),
User(2, 'user2', 'abcxyz'),
]
username_table = u.username: u for u in users
userid_table = u.id: u for u in users
def authenticate(username, password):
user = username_table.get(username, None)
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
return user
def identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)
app.config['SECRET_KEY'] = 'super-secret'
jwt = JWT(app, authenticate, identity)
def checkuser(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_identity.username == 'user1':
return func(*args, **kwargs)
return abort(401)
return wrapper
class HelloWorld(Resource):
decorators = [checkuser, jwt_required()]
def get(self):
return 'hello': current_identity.username
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True)
发布
"username": "user1",
"password": "abcxyz"
发送至localhost:5000/auth
并获得access_token
作为回复。
然后 GET localhost:5000/
带标题
Authorization: JWT `the access_token value above`
你会得到
"hello": "user1"
如果您尝试使用 user2 的 JWT 令牌访问 localhost:5000/
,您将获得 401
。
装饰器是这样包装的:
for decorator in self.decorators:
resource_func = decorator(resource_func)
https://github.com/flask-restful/flask-restful/blob/master/flask_restful/init.py#L445
所以装饰器数组中后面的那个可以更早地运行。
更多参考:
https://github.com/rchampa/timetable/blob/master/restful/users.py
https://github.com/mattupstate/flask-jwt/issues/37
【讨论】:
你读过问题吗?问题是关于在装饰器中访问current_identity
。
current_identity
如果被 jwt_required
装饰器包裹,则可以访问。为什么需要定制装饰器?
装饰器可用于多种情况。例如 - 检测用户的角色。为了避免在所有视图中重写代码,您可以创建像“detect_role”这样的装饰器。请阅读 OP 的整个问题。
好的,我明白你的意思了。我会尽快解决这个问题(在我自己的项目中)。我已经阅读了jwt_required
的源代码,current_identity 被注入到其中的堆栈中。因此,对于您自己的装饰器,我认为 current_identity 可以访问jwt_required
。
@NikolayFominyh 我已经使用带有 current_identity 的自定义装饰器编辑了我的示例。祝你满意,:)【参考方案3】:
使用这个:
from flask_jwt import current_identity
@jwt_required()
def get(self):
return 'current_identity': current_identity.json()
【讨论】:
在回答主题时,最好避免使用某些短语,例如“试试这个”、“使用这个”等。 至于 2020 年,这个方案行不通。并将产生:TypeError: jwt_required() missing 1 required positional argument: 'fn'以上是关于Flask-restful API 授权。在装饰器中访问 current_identity的主要内容,如果未能解决你的问题,请参考以下文章
使用flask-jwt-extended回调与flask-restful和create_app