大多數使用 JWT 的項目其實都是使用的 JWS(JSON Web Signature), JWT 并不等于 JWS,JWS 只是JWT的一種實現,除了 JWS 外,JWE(JSON Web Encryption) 也是JWT的一種實現。
一般我們使用 JWS 完全滿足我們的需求。
安裝依賴
```
(.venv) root@airvip:~/python_app/flask-demo# pip install flask-jwt-extended
```
常用配置
* `JWT_REFRESH_TOKEN_EXPIRES` 刷新 token 過期時間,默認 30 天。
* `JWT_ACCESS_TOKEN_EXPIRES` token 過期時間,默認 15 分鐘
* `JWT_SECRET_KEY` 基于對稱的簽名算法所需的密鑰,如 : `HS*`。如果沒有設置,則使用 flask 的 `SECRET_KEY`值。
修改配置文件 `config.py`
```
class Config(object):
SECRET_KEY?=?"AIRVip123456airvip"
# 數據庫、cache 配置省略
# JWT
JWT_ACCESS_TOKEN_EXPIRES = 7200
```
對 `app` 目錄下的 ` __init__` 初始化文件進行如下改造,添加 `jwt` 模塊
```
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_caching import Cache
from flask_jwt_extended import JWTManager
import logging
from logging.handlers import RotatingFileHandler
from config import config_map
from app.utils.commons import ReCoverter
db = SQLAlchemy()
cache = Cache()
# 創建 jwt 對象
jwt = JWTManager()
logging.basicConfig(level=logging.ERROR)
file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024, backupCount=10)
formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
file_log_handler.setFormatter(formatter)
logging.getLogger().addHandler(file_log_handler)
def create_app(config_name):
app = Flask(__name__)
config_class = config_map[config_name]
app.config.from_object(config_class)
db.init_app(app)
cache.init_app(app)
# 使用 app 初始化 jwt
jwt.init_app(app)
app.url_map.converters["re"] = ReCoverter
from app import api_1_0
app.register_blueprint(api_1_0.bp, url_prefix="/api/v1.0")
from app import html
app.register_blueprint(html.html)
return app
```
對 `app/api_1_0/controller` 下的 `passport.py` 控制器進行如下改造
```
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
from app.api_1_0 import bp
from app import cache
from flask import request,jsonify
from flask_jwt_extended import (
create_access_token, create_refresh_token, jwt_refresh_token_required,
jwt_required, get_jwt_identity
)
@bp.route('/login', methods=['POST'])
def login():
username = request.get_json()['username']
# 驗證自己做
ret = {
"access_token": create_access_token(identity=username),
"refresh_token": create_refresh_token(identity=username)
}
return jsonify(ret)
@bp.route('/refresh', methods=['POST'])
@jwt_refresh_token_required
def refresh():
current_user = get_jwt_identity()
ret = {
'access_token': create_access_token(identity=current_user)
}
return jsonify(ret)
@bp.route('/protected', methods=['GET'])
@jwt_required
def protected():
username = get_jwt_identity()
return jsonify(username=username)
```
**測試**
```
root@airvip:~# curl 127.0.0.1:5000/api/v1.0/login -X POST -H 'Content-Type: application/json' -d '{"username":"airvip"}'
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA0MjA4ODUsIm5iZiI6MTYxMDQyMDg4NSwianRpIjoiZjQwOGY0YWYtMTllMi00NzU1LWE4NWQtNzMxOTdlNzg1YjMxIiwiZXhwIjoxNjEwNDI4MDg1LCJpZGVudGl0eSI6ImFpcnZpcCIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyJ9.vmgt9EMqJjP17XQ7Sxcc1fU5FIsNzrKEvCPqZLBwCgM",
"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA0MjA4ODUsIm5iZiI6MTYxMDQyMDg4NSwianRpIjoiNDk2YjgyMjItYTI3My00MGNhLWJlMmMtMTZkMWQ1MDU5YzY5IiwiZXhwIjoxNjEzMDEyODg1LCJpZGVudGl0eSI6ImFpcnZpcCIsInR5cGUiOiJyZWZyZXNoIn0.JiSlX81RR4QctrYq4jdThEIYGEIWHbgZi94Ra09U7c8"
}
# 刷新 token ,發送 http 請求的 header 用的是登錄返回的 refresh_token
root@airvip:~# curl 127.0.0.1:5000/api/v1.0/refresh -X POST -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA0MjA4ODUsIm5iZiI6MTYxMDQyMDg4NSwianRpIjoiNDk2YjgyMjItYTI3My00MGNhLWJlMmMtMTZkMWQ1MDU5YzY5IiwiZXhwIjoxNjEzMDEyODg1LCJpZGVudGl0eSI6ImFpcnZpcCIsInR5cGUiOiJyZWZyZXNoIn0.JiSlX81RR4QctrYq4jdThEIYGEIWHbgZi94Ra09U7c8'
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA0MjA5MTAsIm5iZiI6MTYxMDQyMDkxMCwianRpIjoiN2Q5ZDBhOWYtZjc1OC00NzBmLThlNjUtZDQyMjk2YTgxZTRhIiwiZXhwIjoxNjEwNDI4MTEwLCJpZGVudGl0eSI6ImFpcnZpcCIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyJ9.xj71pS97iXEVR68BPy1H8pKs82bITnqhOdLbHjlIr3w"
}
# 之后需要驗證的方法,發送 http 請求的 header 用 access_token
root@airvip:~# curl 127.0.0.1:5000/api/v1.0/protected -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTA0MjA5MTAsIm5iZiI6MTYxMDQyMDkxMCwianRpIjoiN2Q5ZDBhOWYtZjc1OC00NzBmLThlNjUtZDQyMjk2YTgxZTRhIiwiZXhwIjoxNjEwNDI4MTEwLCJpZGVudGl0eSI6ImFpcnZpcCIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyJ9.xj71pS97iXEVR68BPy1H8pKs82bITnqhOdLbHjlIr3w'
{
"username": "airvip"
}
root@airvip:~#
```

傳送門:[Flask-JWT-Extended’s 更多使用技巧](https://flask-jwt-extended.readthedocs.io/en/latest/)