之前用的Flask-JWT,感觉不好用,不太灵活,参考Flask-JWT源码直接用jwt库,就方便很多了,python3.7代码封装如下:
# decorator.py
import jwt
from datetime import datetime
from functools import wraps
from werkzeug.local import LocalProxy
from flask import request, jsonify, _request_ctx_stack, current_app
# LocalProxy的使用说明,很好的一篇文章:https://www.jianshu.com/p/3f38b777a621
current_identity = LocalProxy(lambda: getattr(_request_ctx_stack.top, 'current_identity', None))
def jwt_payload(identity):
iat = datetime.utcnow()
exp = iat + current_app.config.get('JWT_EXPIRATION_DELTA')
return {'exp': exp, 'iat': iat, 'identity': identity }
def jwt_encode(identity):
secret = current_app.config['JWT_SECRET_KEY']
algorithm = current_app.config['JWT_ALGORITHM']
required_claims = current_app.config['JWT_REQUIRED_CLAIMS']
payload = jwt_payload(identity)
missing_claims = list(set(required_claims) - set(payload.keys()))
if missing_claims:
raise RuntimeError('Payload is missing required claims: %s' % ', '.join(missing_claims))
return jwt.encode(payload, secret, algorithm=algorithm, headers=None)
def jwt_decode(token):
secret = current_app.config['JWT_SECRET_KEY']
algorithm = current_app.config['JWT_ALGORITHM']
leeway = current_app.config['JWT_LEEWAY']
verify_claims = current_app.config['JWT_VERIFY_CLAIMS']
required_claims = current_app.config['JWT_REQUIRED_CLAIMS']
options = {
'verify_' + claim: True
for claim in verify_claims
}
options.update({
'require_' + claim: True
for claim in required_claims
})
return jwt.decode(token, secret, options=options, algorithms=[algorithm], leeway=leeway)
def jwt_required(fn):
@wraps(fn)
def wapper(*args, **kwargs):
auth_header_value = request.headers.get('Authorization', None)
if not auth_header_value:
return jsonify(code='2100', msg='Authorization缺失')
parts = auth_header_value.split()
if len(parts) == 1:
return jsonify(code='2100', msg='Token缺失') # code 仅作为示例
elif len(parts) > 2:
return jsonify(code='2100', msg='Token无效')
token = parts[1]
if token is None:
return jsonify(code='2100', msg='Token异常')
try:
payload = jwt_decode(token)
except jwt.InvalidTokenError as e:
return jsonify(code='2100', msg=str(e))
_request_ctx_stack.top.current_identity = payload.get('identity')
if payload.get('identity') is None:
return jsonify(code='2100', msg='用户不存在')
return fn(*args, **kwargs)
return wapper
自定义login,及使用示例
# views.py
from werkzeug.security import check_password_hash
from decorator import jwt_required, jwt_encode, current_identity
@mod.route('/login', methods=['POST'])
def login():
name = request.json.get('name', '')
password = request.json.get('password', '')
if len(name) == 0 or len(password) == 0:
return jsonify(code=RETCODE.LOGINERR, msg='请输入正确的用户名或密码')
manager = Manager.query.filter(Manager.name==name).first() # Manager仅作为示例
if not manager:
return jsonify(code='2100', msg='未找到用户')
if not check_password_hash(manager.password, password):
return jsonify(code=RETCODE.PWDERR, msg='密码错误')
token = jwt_encode({ 'mid': manager.id, 'name': manager.name})
return jsonify(code=RETCODE.OK, data={'token': token.decode('utf8')})
@mod.route('/token/update', methods=['POST'])
@jwt_required
def update_token():
token = jwt_encode({ 'mid': current_identity.get('mid'), 'name': current_identity.get('name')})
return jsonify(code=RETCODE.OK, data={'token': token.decode('utf8')})
配置信息如下:
# config.py
JWT_SECRET_KEY = "alita666666"
JWT_EXPIRATION_DELTA = timedelta(seconds=3600*48)
JWT_VERIFY_CLAIMS = ['signature', 'exp', 'iat']
JWT_REQUIRED_CLAIMS = ['exp', 'iat']
JWT_AUTH_ENDPOINT = 'jwt'
JWT_ALGORITHM = 'HS256'
JWT_LEEWAY = timedelta(seconds=10)
JWT_AUTH_HEADER_PREFIX = 'JWT'
JWT_NOT_BEFORE_DELTA = timedelta(seconds=0)
end