• flask_jwt_extended 4.0学习


    1.前言

           在学习flask_jwt_extended插件的时候遇到许多问题,究其原因是因为版本问题,4.0以后的版本在语法上做了许多优化,而我是参考较低版本的flask_jwt_extended学习的,一开始不明白出问题的原因,所以参考了许许多多博友的思路和写法,发现并没有问题,后来想到去看官方的文档,发现了原因所在,以下是4.0版本的api变化对照,更加详细的变化请参官方文档

    flask_jwt_extended4.0版本API的变化

    2.flask_jwt_extended的常规使用

    1. create_access_token()用来创建令牌

    2. get_jwt_identity()用来根据令牌取得之前的identity信息

    3. jwt_required()这是一个装饰器,用来保护flask节点(endpoint)

     1 from flask_jwt_extended import create_access_token
     2 from flask_jwt_extended import jwt_required
     3 from flask_jwt_extended import get_jwt_identity
     4 from flask_jwt_extended import JWTManager
     5 
     6 from flask import Flask
     7 from flask import jsonify
     8 from datetime import timedelta
     9 
    10 app = Flask(__name__)
    11 jwt = JWTManager(app)
    12 
    13 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密盐值
    14 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 设置token的有效时间,默认15分钟
    15 
    16 @app.route('/login',methods=['POST'])
    17 def login():
    18     # 携带用户标识生成token
    19     access_token = create_access_token(identity={'id': 1, 'username': 'apple'})
    20     return jsonify(access_token=access_token)
    21 
    22 @app.route('/index',methods=['GET'])
    23 @jwt_required() # token认证,老版本不需要括号
    24 def index():
    25     # 获取认证标识中的信息
    26     userInfo = get_jwt_identity()
    27     return jsonify(userInfo)
    28 
    29 if __name__ == '__main__':
    30     app.run()

      注意:请求时,headers需要携带Authorization参数,其值为Bearer+空格 +token的字符串。

    3.刷新token

           说道刷新token,我在学习的时候有些问题并没有想明白,并不是对技术的疑问,而是不清楚为什么要这样做。我了解到为了解决access_token过期问题,会设置在用户登入的同时返回一个refresh_token用于access_token的刷新,而refresh_token的有效期的设置会比access_token的时间长,所以我疑问的是,如果说仅仅是解决过期时间的问题, 可以直接将 access_token 的过期时间延长就行了, 但它确是以刷新 token 的方式让使用者以无感的方式延长 access_token 的有效期。

      我所查的资料无非是说:一,提高用户体验感,免登入;二、安全,token如果设置太长时间会不安全。但是笔者觉得直接去设置延长access_token的时间,和刷新token变相延长token的时间,本质并没有不同,只不过后者是在一段时间内,换了多个token,但其用于刷新access_token的refresh_token为啥就不会造成不安全的因素了?这就好比是把access_token的时间给了refresh_token,而在用refresh_token去刷新access_token,饶了个圈子而已,哎..没想明白...

     1 from flask_jwt_extended import create_access_token
     2 from flask_jwt_extended import create_refresh_token
     3 from flask_jwt_extended import get_jwt_identity
     4 from flask_jwt_extended import jwt_required
     5 from flask_jwt_extended import JWTManager
     6 
     7 from datetime import timedelta
     8 from flask import Flask
     9 from flask import jsonify
    10 
    11 app = Flask(__name__)
    12 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密盐值
    13 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 设置access_token的有效时间
    14 app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=1)  # 设置refresh_token的有效时间
    15 jwt = JWTManager(app)
    16 
    17 @app.route("/login", methods=["POST"])
    18 def login():
    19     access_token = create_access_token(identity={'user': 'apple', 'id': 1})
    20     refresh_token = create_refresh_token(identity={'user': 'apple', 'id': 1})
    21     return jsonify(access_token=access_token, refresh_token=refresh_token)
    22 
    23 # 携带refresh_token请求此接口,刷新获的新的access_token
    24 @app.route("/refresh", methods=["POST"])
    25 @jwt_required(refresh=True) # 刷新token的装饰器,这是最新的写法
    26 def refresh():
    27     identity = get_jwt_identity()
    28     access_token = create_access_token(identity=identity)
    29     return jsonify(access_token=access_token)
    30 
    31 @app.route("/protected", methods=["GET"])
    32 @jwt_required()
    33 def protected():
    34     current_user = get_jwt_identity()
    35     return jsonify(current_user=current_user)
    36 
    37 if __name__ == "__main__":
    38     app.run()

         生成token响应如下。在这里需要说一下的是access_token只能用于携带访问,并不能用于刷新token;同理refresh_token只能用于刷新,不能用于携带请求获取数据

    4.自定义装饰器

      @jwt_required()只能校验当前请求的用户是否携带token,如果需要校验更多则可以自定义装饰器

     1 from flask_jwt_extended import create_access_token
     2 from flask_jwt_extended import get_jwt
     3 from flask_jwt_extended import JWTManager
     4 from flask_jwt_extended import verify_jwt_in_request
     5 
     6 from functools import wraps
     7 from datetime import timedelta
     8 from flask import Flask
     9 from flask import jsonify
    10 
    11 app = Flask(__name__)
    12 jwt = JWTManager(app)
    13 app.config["JWT_SECRET_KEY"] = "hello@#$%&"
    14 app.config['JWT_ACCESS_TOKEN_EXPIRES']=timedelta(hours=1)
    15 
    16 # 它验证请求中是否存在JWT和请求用户是否管理员
    17 def admin_required():
    18     def wrapper(fn):
    19         @wraps(fn)
    20         def decorator(*args, **kwargs):
    21             verify_jwt_in_request()
    22             claims = get_jwt()
    23             if claims["is_administrator"]:
    24                 return fn(*args, **kwargs)
    25             else:
    26                 return jsonify(msg="Admins only!"), 403
    27         return decorator
    28     return wrapper
    29 
    30 
    31 @app.route("/login", methods=["POST"])
    32 def login():
    33     access_token = create_access_token(
    34         identity={'id': 1, 'username': 'apple'},
    35         additional_claims={"is_administrator": True}
    36     )
    37     return jsonify(access_token=access_token)
    38 
    39 
    40 
    41 @app.route("/protected", methods=["GET"])
    42 @admin_required()  # 使用自定义装饰器
    43 def protected():
    44     additional_claims = get_jwt()
    45     return jsonify(claims=additional_claims)
    46 
    47 if __name__ == "__main__":
    48     app.run()

    这里有些需要说明的地方:

    1、verify_jwt_in_request() 不返回任何东西。如果令牌解码链中的任何事情失败,它将返回一个适当的异常。也就是说它在这里的作用是为了校验当前请求有没有token,如果没有则会返回一个异常信息,不能访问被保护的节点,同样的和@jwt_required()它们一样有四个可选的参数:optional=False, fresh=False, refresh=False, locations=None,这里暂时不说,后面统一描述。

    2、@wraps(fn)的作用,它来自from functools import wraps,它的作用是消除装饰器带来的副作用。因为在使用装饰器的时候,其实函数名和函数的doc已经发生了变化,而加上它则可以和好的解决这些问题,这也是使用装饰器的一个小技巧。

    3、additional_claims参数的用法,我们知道除去存放基本的用户的标识identity外,在access_token中还可能存放其他的信息,这个时候就可以使用这个参数了,它的作用和@jwt.additional_claims_loader装饰器的作用是一样的,都是将信息存储到access_token中。但有些不同的是,该函数在create_access_token()函数被调用后使用。

    4、get_jwt()用于获取access_token存储的信息,在之前的版本它是get_jwt_claims()

    如果存放其它信息较多的情况下,则可以使用装饰器的方式添加:

     1 @app.route("/login", methods=["POST"])
     2 def login():
     3     access_token = create_access_token(
     4         identity={'id': 1, 'username': 'apple'}
     5         # additional_claims={"is_administrator": True}
     6     )
     7     return jsonify(access_token=access_token)
     8 
     9 @jwt.additional_claims_loader
    10 def add_claims_to_access_token(identity):#参数identity,就是access_token中的用户标识,该参数必须填
    11     return {
    12         'is_administrator': True
    13     }

    5.常用参数描述

    optional:
      描述:如果为True,即使是没有jwt授权,也能够允许访问被保护的的节点,默认为False
    1 @app.route('/index', methods=['GET'])
    2 @jwt_required(optional=True)
    3 def index():
    4     current_user = get_jwt_identity()
    5     if current_user:
    6         return jsonify(msg='授权用户访问'), 200
    7     else:
    8         return jsonify(msg='未授权用户访问'), 200
    fresh:
      描述:如果为True,只能让"新鲜"的授权token访问,如果是刷新后的token则不能访问,该参数要结合create_access_token来使用,当我们创建access_token时,有一个
    可选参数为fresh如果我们把它设为True,则代表它是"新鲜"token,换句话说就是只有用户登入产生的token才具有"新鲜度"。默认为False。
     1 from flask_jwt_extended import create_access_token
     2 from flask_jwt_extended import create_refresh_token
     3 from flask_jwt_extended import get_jwt_identity
     4 from flask_jwt_extended import jwt_required
     5 from flask_jwt_extended import JWTManager
     6 
     7 from datetime import timedelta
     8 from flask import Flask
     9 from flask import jsonify
    10 
    11 app = Flask(__name__)
    12 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密盐值
    13 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 设置access_token的有效时间
    14 app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=1)  # 设置refresh_token的有效时间
    15 jwt = JWTManager(app)
    16 
    17 @app.route("/login", methods=["POST"])
    18 def login():
    19     access_token = create_access_token(identity={'user': 'apple', 'id': 1}, fresh=True)  # 开启新鲜度模式
    20     refresh_token = create_refresh_token(identity={'user': 'apple', 'id': 1})
    21     return jsonify(access_token=access_token, refresh_token=refresh_token)
    22 
    23 @app.route("/refresh", methods=["POST"])  # 携带refresh_token请求此接口,刷新access_token
    24 @jwt_required(refresh=True)
    25 def refresh():
    26     identity = get_jwt_identity()
    27     access_token = create_access_token(identity=identity, fresh=False)  # 刷新不开启新鲜度模式
    28     return jsonify(access_token=access_token)
    29 
    30 @app.route("/protected", methods=["GET"])
    31 @jwt_required(fresh=True)  # 只允许登入授权的用户访问,刷新授权不能在访问
    32 def protected():
    33     identity = get_jwt_identity()
    34     return jsonify(identity=identity)
    35 
    36 @app.route('/index')
    37 @jwt_required()  # 无论登入授权还是,刷新授权都能访问
    38 def index():
    39     return jsonify(msg='欢迎访问')
    40 
    41 if __name__ == "__main__":
    42     app.run()

      这里需要注意的是,刷新toekn视图函数里面的create_access_token的"新鲜度"是不开启的,否则无论是否刷新都能访问,这就失去了参数本身的意义,笔者认为该参数有两个作用,其一是,当每次用户通过提供用户名和密码进行身份验证时,他们都会收到一个可以访问任何路由的新访问令牌。但一段时间后,该令牌将不再被认为是新的,一些关键或危险的路由将被阻止,直到用户再次验证其密码。所有其他路由仍将正常工作,即使用户的令牌不再新鲜。例如,我们可能不允许用户更改他们的电子邮件地址,除非他们有一个新的令牌,但我们允许他们正常使用我们的Flask应用程序的其余部分。其二,可以了防止refresh_token被盗后导致被他人恶意刷新token的危害的发生。

      另外在补充一点,create_access_token的"新鲜度"是可以设定时间的,如fresh=timedelta(seconds=60)把"新鲜度"保持60s的时间,超过这个时间则,失去"新鲜度"

    refresh:
      描述:如果为True,只允许refresh_token访问被保护的节点,如果为False,则access_token才可以访问被保护的节点,默认为False,该参数用于刷新token时需要设置
    1 # 携带refresh_token请求此接口,刷新access_token
    2 @app.route("/refresh", methods=["POST"])
    3 @jwt_required(refresh=True) #只允许刷新token访问
    4 def refresh():
    5     identity = get_jwt_identity()
    6     access_token = create_access_token(identity=identity,fresh=False)
    7     return jsonify(access_token=access_token)
    
    

    locations:

      描述:该参数是4.0新增的,指的是用户携带授权token访问时,其jwt的所处位置列表。例如['headers', 'cookies'],默认为None。什么意思呢?简单的的说你的token是在headers里呢还是cookies,去访问的。当然也可以使用JWT_TOKEN_LOCATION,进行全局配置。

    6.定制token过期返回信息

    1 @jwt.expired_token_loader
    2 def expired_token_callback(jwt_header, jwt_payload):
    3     return jsonify(msg='token过期了哟'),201

    注意的是,以上两个参数是必填的,第一个时包含jwt的header,第二个是包含jwt的payload(载荷)

    其实类似功能的装饰器还有很多,这里不一一描述,更多信息请关注官方文档,如有错误,也请指教!!

    待续......

     
    天青色等烟雨而我在等你!
  • 相关阅读:
    创建ftp站点
    删除文件夹下所有文件
    搭建API Mock
    linux 定时备份数据库
    linux 常用Mysql脚本命令
    离线安装Redis 说明
    离线安装Mariadb
    ffmpeg+nginx 实现rtsp转rtmp并通过nginx转发
    linq和ef关于group by取最大值的两种写法
    Autofac 泛型依赖注入
  • 原文地址:https://www.cnblogs.com/Liu928011/p/15020581.html
Copyright © 2020-2023  润新知