第五章 来自复杂对象的Token

一种非常常见的设置是将用户信息(用户名、密码、角色等)存储在数据库中。现在,假设我们要创建一个访问令牌,其中令牌标识是用户名,我们还希望将用户角色作为附加声明存储在令牌中。我们可以使用上一节讨论的user_claims_loader()装饰器来实现这一点。

  但是,如果我们将用户名传递给`user_claims_loader()`,我们最终需要从数据库查询该用户两次。
  * 1 第一次是当登录端点被点击时,我们需要验证用户名和密码。
  * 2 第二次是在`user_claims_loader()`函数中,因为我们需要查询这个用户的角色。这不是什么大事,但显然它可以更有效率。

在本章的方法中,提供了将任何对象传递给create_access_token()函数的能力,然后将该函数作为user_claims_loader()传递。这允许我们只访问数据库一次,但是引入了一个需要解决的新问题。我们仍然需要从对象中提取用户名,这样我们就可以让用户名作为新令牌的标识。我们可以为此使用第二个装饰器user_identity_loader(),它允许您接收传入create_access_token()的任何对象,并从该对象返回一个json序列化的标识。
请看示例代码:

from flask import Flask, jsonify, request
from flask_jwt_extended import (
    JWTManager, jwt_required, create_access_token,
    get_jwt_identity, get_jwt_claims
)

app = Flask(__name__)

app.config['JWT_SECRET_KEY'] = 'super-secret'  # Change this!
jwt = JWTManager(app)

#创建一个用来构建JWT数据的复杂对象
#看起来有点像SQLAlchemy 的实例
class UserObject:
    def __init__(self, username, roles):
        self.username = username
        self.roles = roles

#定义jwt.user_claims_loader装饰器,该装饰器会在调用create_access_token函数时自动被调用,
#user_claims_loader的参数就是传递给 create_access_token的参数
#user_claims_loader返回的数据会被保存到jwt 中,作为claims存在
@jwt.user_claims_loader
def add_claims_to_access_token(user):
    return {'roles': user.roles}

#user_identity_loader装饰器,该装饰器会在调用create_access_token函数时自动被调用,
#user_identity_loader的参数就是传递给 create_access_token的参数
#user_claims_loader返回的数据会被保存到jwt 中,作为identity存在
@jwt.user_identity_loader
def user_identity_lookup(user):
    return user.username


@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username', None)
    password = request.json.get('password', None)
    if username != 'test' or password != 'test':
        return jsonify({"msg": "Bad username or password"}), 401

    # Create an example UserObject
    user = UserObject(username='test', roles=['foo', 'bar'])

    # We can now pass this complex object directly to the
    # create_access_token method. This will allow us to access
    # the properties of this object in the user_claims_loader
    # function, and get the identity of this object from the
    # user_identity_loader function.
    access_token = create_access_token(identity=user)
    ret = {'access_token': access_token}
    return jsonify(ret), 200


@app.route('/protected', methods=['GET'])
@jwt_required
def protected():
    ret = {
        'current_identity': get_jwt_identity(),  # test
        'current_roles': get_jwt_claims()['roles']  # ['foo', 'bar']
    }
    return jsonify(ret), 200


if __name__ == '__main__':
    app.run()

你可能感兴趣的:(第五章 来自复杂对象的Token)