从刷新令牌创建非新鲜访问令牌时,flask-jwt-extended current_user identity = None
flask-jwt-extended current_user identity = None when creating non-fresh access token from refresh token
在我的 Flask 应用程序 (python 2.7) 中,我试图通过 @jwt.expired_token_loader 装饰器在刷新令牌过期时触发访问令牌进行刷新。访问令牌和刷新令牌都存储在 cookie 中 (JWT_TOKEN_LOCATION = 'cookies')。
我正在使用文档 (https://flask-jwt-extended.readthedocs.io/en/latest/tokens_in_cookies.html) 中提供的相同代码来执行此操作,并且我能够成功生成新的访问令牌。但是,在生成新的访问令牌后,访问令牌的身份声明等于 None (get_raw_jwt())。每当我打印 jwt_claims 或尝试使用 current_user = get_jwt_identity() 获取当前用户时,无论我对刷新令牌或访问令牌做什么 returns身份为 None。了解哪个用户正在向神经网络提交查询对我来说很重要,这样我就可以正确地跟踪哪些用户提交了哪些查询(一对多关系)。
我已尝试对 refresh_token 和 运行 进行故障排除(解码),并将其分为一个单独的问题:当我尝试使用 decode_token() 对 refresh_token 进行解码时,我得到一个以 InvalidSignatureError 结尾的长回溯:签名验证失败。我使用 refresh_token 和 运行 它通过 https://jwt.io and it decodes the token. I can see in the decoded token that the "identity" claim is providing me the user's identity, but it tells me that the token is not verified. However, once I check the secret base64 encoded box on the screen the signature becomes verified and the signature portion of the jwt changed along with it. I attempt to decode this modified jwt that https://jwt.io 为我提供了 decode_token 函数,但它仍然向我提供相同的错误:InvalidSignatureError:签名验证失败。
我花了几个小时阅读 google 提供给我的关于 flask-jet-extended 和 PyJWT 的所有内容,但我不知道如何修复它。我已经尝试将我的 JWT_SECRET_KEY 配置修改为不同的字符串,甚至使用 base64 和 none 对其进行编码可以解决问题。我已经打开和关闭 JWT_COOKIE_CSRF_PROTECT 配置。我打开和关闭 JWT_ACCESS_COOKIE_PATH 并且 JWT_REFRESH_COOKIE_PATH.I 尝试解码 refresh_token_cookie 和 CSRF_refresh_token cookie。我在提供 csrf_value=request.cookies.get('csrftoken') 参数。我尝试直接使用 jwt 的 decode() 函数(
来自 jwt 导入解码)。
我只是不知道还能做什么,也找不到任何其他在线资源。非常感谢任何帮助!!
我的下一步是将我的身份验证系统移动到 flask-jet-simple 或 PyJWT。我真的很想使用 JWT 来验证我的用户。我不知道如何将 JWT 与 flask-login 结合起来,或者这是否可能。我无法在网上找到任何有人使用 JWT 的 flask-login 的资源。我确实找到了一个名为 flask-jwt-login 的最近的回购协议,如果我无法弄清楚,我可能会尝试使用它。最终我想继续使用 flask-jwt-extended。我还有此 Web 应用程序的其他部分需要重点关注,并希望解决他的问题。
无论如何,这是我的代码,工作流从 /login 页面开始。这会将您重定向到 /NN 页面。访问令牌过期后,如果您尝试重新加载 /NN 页面,它将自行重新路由到 /token/refresh 页面。刷新令牌后,它将 return 返回 /NN 页面。
如果我需要上传任何其他文件,请告诉我。
P.S。这是我第一次 post 关于堆栈溢出,请原谅我的任何格式问题。
application.py
from flask import url_for,render_template, redirect,request, jsonify,flash,\
make_response, session
from flask_jwt_extended import (create_access_token, create_refresh_token,
jwt_required, get_jwt_identity, get_jwt_claims,get_current_user,
set_access_cookies,set_refresh_cookies,
unset_jwt_cookies, get_raw_jwt, jwt_refresh_token_required,decode_token)
from jwt import decode
from forms import RegisterForm, LoginForm, NNForm
from models import Users
from website import app,db,jwt
#ToDo When the token expires I get an HTTP status code of 401 I can use expired_token_loader refresh token.
@app.route('/token/refresh', methods=['GET','POST'])
@jwt_refresh_token_required
@jwt.expired_token_loader
def refresh():
#Create the new access token
ref_token = request.cookies.get('refresh_token_cookie')
csrftoken = request.cookies.get('csrftoken')
decode_ref_token = decode_token(ref_token)
current_user = get_jwt_identity()
print('ref_token:', ref_token)
print('current_user:', current_user, get_raw_jwt())
access_token = create_access_token(identity=current_user)
#Set the JWT access cookie in the response
print('from refresh():', request.url)
response = make_response(redirect(request.url))
set_access_cookies(response,access_token)
#set_refresh_cookies()
return response
@app.route('/token/remove', methods=['POST'])
def logout():
#ToDo Still need to build the logout page.
response = make_response(redirect(url_for('logout_page')))
unset_jwt_cookies(response)
return response
@app.route('/register/', methods=['GET','POST'])
def register_page():
form = RegisterForm(request.form)
print( request.method, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
user = Users(form.first_name.data, form.last_name.data, \
form.email.data, form.password.data, form.organization.data)
user.save_to_db()
flash("Thanks for Registering. Please login")
return redirect(url_for("NN_page"))
return render_template('register.html',form=form)
@app.route('/login/', methods=['GET','POST'])
def login_page():
form = LoginForm(request.form)
print(request.method, request.form)
if request.method == "POST":
#This checks if the user is in the db and returns the user obj.
user = form.validate_on_submit()
if user:
access_token = create_access_token(identity=user.email, fresh=True)
refresh_token = create_refresh_token(identity=user.email)
response = make_response(redirect(url_for('NN_page')))
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
#response.headers['Authorization'] = 'Bearer {}'.format(access_token)
print(response)
return response
#return jsonify({'access_token':access_token})
#return redirect((url_for("NN_page")))
return render_template('login_page.html', form=form)
@jwt.invalid_token_loader #This allows me to stop people who have not logged in yet.
def missing_JWT_token(msg):
print('from missing_JWT_token:', msg)
return redirect(url_for('login_page'))
# return "The site being accessed requires a valid JWT to view." \
# "Error: {}".format(msg)
@app.route('/NN/', methods=['GET','POST'])
@jwt_required
def NN_page():
jwt_claims = get_raw_jwt()
print(jwt_claims)
print('cookie keys:', request.cookies.get('refresh_token_cookie'))
user = get_jwt_identity()
print('User:',user)
form = NNForm(request.form, headers=request.headers)
print(request.form, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
return redirect((url_for("success_NN_submission")))
return render_template('NN_page.html', form=form)
config.py
import os
from datetime import timedelta
from base64 import b64encode
secret_key = os.urandom(24)
jwt_secret_key = b64encode('I_love_my_smokes!')
class BaseConfig(object):
SECRET_KEY = secret_key
SQLALCHEMY_DATABASE_URI = 'sqlite:///Protein_NN.db'
SQLALCHEMY_TRACK_MODIFICATION = False
#JWT_SECRET_KEY = jwt_secret_key
JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=10)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(minutes=1)
JWT_TOKEN_LOCATION = 'cookies'
#JWT_ACCESS_COOKIE_PATH = '/NN/'
#JWT_REFRESH_COOKIE_PATH ='/token/refresh'
JWT_COOKIE_CSRF_PROTECT = False
SESSION_COOKIE_SECURE = True
class DevelopmentConfig(BaseConfig):
DEBUG = True
JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=5)
SESSION_COOKIE_SECURE = False
#PROPOGATE_EXCEPTION = True
#EMAIL SETTINGS
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
#MAIL_PORT = 587 # This is for TLS
MAIL_USE_TLS = False
MAIL_USE_SSL = True
#MAIL_USERNAME = os.environ['EMAIL_USER']
#MAIL_PASSWORD = os.environ['EMAIL_PASSWORD']
#BOOTSTRAP_SERVE_LOCAL = True
这是访问令牌被刷新令牌刷新后 get_raw_jwt() returns 的内容。
{'user_claims': {}, u'jti': u'9fb01b6c-619b-4fe6-91d3-73f8609f2f61',
u'exp': 1547022397, u'iat': 1547022392, u'fresh': False,
u'type': u'access', u'nbf': 1547022392, u'identity': None}
如您所见,身份声明等于 None。
这是我看到的回溯:
Traceback (most recent call last):
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1719, in handle_user_exception
return handler(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask_jwt_extended/jwt_manager.py", line 93, in handle_expired_error
return self._expired_token_callback()
File "/Users/Danny/Documents/Codes/Ellington/NN App/website/application.py", line 43, in refresh
print('current_user:', current_user, decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNzVlNWExMy1mNjRiLTQxNmItOTY0ZC0wMDg5ODI4NGY2NGQiLCJleHAiOjE1NDcwMTk5ODUsImlhdCI6MTU0NzAxOTkyNSwidHlwZSI6InJlZnJlc2giLCJuYmYiOjE1NDcwMTk5MjUsImlkZW50aXR5IjoiZGFubnlAbWUuY29tIn0.LVEj6As2Uh_xgTbjm94b0M6mJeD0YLkf9KpgNKTZJOw'))
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jwt.py", line 92, in decode
jwt, key=key, algorithms=algorithms, options=options, **kwargs
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 156, in decode
key, algorithms)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 223, in _verify_signature
raise InvalidSignatureError('Signature verification failed')
InvalidSignatureError: Signature verification failed
在刷新函数上使用这两个独立的装饰器不会按照您想要的方式工作。过期的加载器装饰器不会设置当前用户,因为调用该回调函数时 jwt 无效。
而是尝试将刷新代码分解为两个装饰器独立使用的辅助函数:
def refresh_token(username):
# return flask response from here
@jwt.expired_token_loader
def handle_expired_token():
# get username here from raw jwt
username = 'todo'
return refresh_token(username)
@app.route(‘/refresh)
@jwt_refresh_token_required
def refresh_endpoint():
username = get_current_identity()
return refresh_token(username)
您也可以使用自定义装饰器代替 jwt_required 装饰器并实现类似的效果。这里讨论了一些例子:https://gitter.im/flask-jwt-extended/Lobby?at=5c1a9b37c35a3002474ddf3d
在我的 Flask 应用程序 (python 2.7) 中,我试图通过 @jwt.expired_token_loader 装饰器在刷新令牌过期时触发访问令牌进行刷新。访问令牌和刷新令牌都存储在 cookie 中 (JWT_TOKEN_LOCATION = 'cookies')。 我正在使用文档 (https://flask-jwt-extended.readthedocs.io/en/latest/tokens_in_cookies.html) 中提供的相同代码来执行此操作,并且我能够成功生成新的访问令牌。但是,在生成新的访问令牌后,访问令牌的身份声明等于 None (get_raw_jwt())。每当我打印 jwt_claims 或尝试使用 current_user = get_jwt_identity() 获取当前用户时,无论我对刷新令牌或访问令牌做什么 returns身份为 None。了解哪个用户正在向神经网络提交查询对我来说很重要,这样我就可以正确地跟踪哪些用户提交了哪些查询(一对多关系)。
我已尝试对 refresh_token 和 运行 进行故障排除(解码),并将其分为一个单独的问题:当我尝试使用 decode_token() 对 refresh_token 进行解码时,我得到一个以 InvalidSignatureError 结尾的长回溯:签名验证失败。我使用 refresh_token 和 运行 它通过 https://jwt.io and it decodes the token. I can see in the decoded token that the "identity" claim is providing me the user's identity, but it tells me that the token is not verified. However, once I check the secret base64 encoded box on the screen the signature becomes verified and the signature portion of the jwt changed along with it. I attempt to decode this modified jwt that https://jwt.io 为我提供了 decode_token 函数,但它仍然向我提供相同的错误:InvalidSignatureError:签名验证失败。
我花了几个小时阅读 google 提供给我的关于 flask-jet-extended 和 PyJWT 的所有内容,但我不知道如何修复它。我已经尝试将我的 JWT_SECRET_KEY 配置修改为不同的字符串,甚至使用 base64 和 none 对其进行编码可以解决问题。我已经打开和关闭 JWT_COOKIE_CSRF_PROTECT 配置。我打开和关闭 JWT_ACCESS_COOKIE_PATH 并且 JWT_REFRESH_COOKIE_PATH.I 尝试解码 refresh_token_cookie 和 CSRF_refresh_token cookie。我在提供 csrf_value=request.cookies.get('csrftoken') 参数。我尝试直接使用 jwt 的 decode() 函数( 来自 jwt 导入解码)。
我只是不知道还能做什么,也找不到任何其他在线资源。非常感谢任何帮助!!
我的下一步是将我的身份验证系统移动到 flask-jet-simple 或 PyJWT。我真的很想使用 JWT 来验证我的用户。我不知道如何将 JWT 与 flask-login 结合起来,或者这是否可能。我无法在网上找到任何有人使用 JWT 的 flask-login 的资源。我确实找到了一个名为 flask-jwt-login 的最近的回购协议,如果我无法弄清楚,我可能会尝试使用它。最终我想继续使用 flask-jwt-extended。我还有此 Web 应用程序的其他部分需要重点关注,并希望解决他的问题。
无论如何,这是我的代码,工作流从 /login 页面开始。这会将您重定向到 /NN 页面。访问令牌过期后,如果您尝试重新加载 /NN 页面,它将自行重新路由到 /token/refresh 页面。刷新令牌后,它将 return 返回 /NN 页面。
如果我需要上传任何其他文件,请告诉我。
P.S。这是我第一次 post 关于堆栈溢出,请原谅我的任何格式问题。
application.py
from flask import url_for,render_template, redirect,request, jsonify,flash,\
make_response, session
from flask_jwt_extended import (create_access_token, create_refresh_token,
jwt_required, get_jwt_identity, get_jwt_claims,get_current_user,
set_access_cookies,set_refresh_cookies,
unset_jwt_cookies, get_raw_jwt, jwt_refresh_token_required,decode_token)
from jwt import decode
from forms import RegisterForm, LoginForm, NNForm
from models import Users
from website import app,db,jwt
#ToDo When the token expires I get an HTTP status code of 401 I can use expired_token_loader refresh token.
@app.route('/token/refresh', methods=['GET','POST'])
@jwt_refresh_token_required
@jwt.expired_token_loader
def refresh():
#Create the new access token
ref_token = request.cookies.get('refresh_token_cookie')
csrftoken = request.cookies.get('csrftoken')
decode_ref_token = decode_token(ref_token)
current_user = get_jwt_identity()
print('ref_token:', ref_token)
print('current_user:', current_user, get_raw_jwt())
access_token = create_access_token(identity=current_user)
#Set the JWT access cookie in the response
print('from refresh():', request.url)
response = make_response(redirect(request.url))
set_access_cookies(response,access_token)
#set_refresh_cookies()
return response
@app.route('/token/remove', methods=['POST'])
def logout():
#ToDo Still need to build the logout page.
response = make_response(redirect(url_for('logout_page')))
unset_jwt_cookies(response)
return response
@app.route('/register/', methods=['GET','POST'])
def register_page():
form = RegisterForm(request.form)
print( request.method, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
user = Users(form.first_name.data, form.last_name.data, \
form.email.data, form.password.data, form.organization.data)
user.save_to_db()
flash("Thanks for Registering. Please login")
return redirect(url_for("NN_page"))
return render_template('register.html',form=form)
@app.route('/login/', methods=['GET','POST'])
def login_page():
form = LoginForm(request.form)
print(request.method, request.form)
if request.method == "POST":
#This checks if the user is in the db and returns the user obj.
user = form.validate_on_submit()
if user:
access_token = create_access_token(identity=user.email, fresh=True)
refresh_token = create_refresh_token(identity=user.email)
response = make_response(redirect(url_for('NN_page')))
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
#response.headers['Authorization'] = 'Bearer {}'.format(access_token)
print(response)
return response
#return jsonify({'access_token':access_token})
#return redirect((url_for("NN_page")))
return render_template('login_page.html', form=form)
@jwt.invalid_token_loader #This allows me to stop people who have not logged in yet.
def missing_JWT_token(msg):
print('from missing_JWT_token:', msg)
return redirect(url_for('login_page'))
# return "The site being accessed requires a valid JWT to view." \
# "Error: {}".format(msg)
@app.route('/NN/', methods=['GET','POST'])
@jwt_required
def NN_page():
jwt_claims = get_raw_jwt()
print(jwt_claims)
print('cookie keys:', request.cookies.get('refresh_token_cookie'))
user = get_jwt_identity()
print('User:',user)
form = NNForm(request.form, headers=request.headers)
print(request.form, form.validate_on_submit())
if request.method == "POST" and form.validate_on_submit():
return redirect((url_for("success_NN_submission")))
return render_template('NN_page.html', form=form)
config.py
import os
from datetime import timedelta
from base64 import b64encode
secret_key = os.urandom(24)
jwt_secret_key = b64encode('I_love_my_smokes!')
class BaseConfig(object):
SECRET_KEY = secret_key
SQLALCHEMY_DATABASE_URI = 'sqlite:///Protein_NN.db'
SQLALCHEMY_TRACK_MODIFICATION = False
#JWT_SECRET_KEY = jwt_secret_key
JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=10)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(minutes=1)
JWT_TOKEN_LOCATION = 'cookies'
#JWT_ACCESS_COOKIE_PATH = '/NN/'
#JWT_REFRESH_COOKIE_PATH ='/token/refresh'
JWT_COOKIE_CSRF_PROTECT = False
SESSION_COOKIE_SECURE = True
class DevelopmentConfig(BaseConfig):
DEBUG = True
JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=5)
SESSION_COOKIE_SECURE = False
#PROPOGATE_EXCEPTION = True
#EMAIL SETTINGS
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
#MAIL_PORT = 587 # This is for TLS
MAIL_USE_TLS = False
MAIL_USE_SSL = True
#MAIL_USERNAME = os.environ['EMAIL_USER']
#MAIL_PASSWORD = os.environ['EMAIL_PASSWORD']
#BOOTSTRAP_SERVE_LOCAL = True
这是访问令牌被刷新令牌刷新后 get_raw_jwt() returns 的内容。
{'user_claims': {}, u'jti': u'9fb01b6c-619b-4fe6-91d3-73f8609f2f61',
u'exp': 1547022397, u'iat': 1547022392, u'fresh': False,
u'type': u'access', u'nbf': 1547022392, u'identity': None}
如您所见,身份声明等于 None。
这是我看到的回溯:
Traceback (most recent call last):
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask/app.py", line 1719, in handle_user_exception
return handler(e)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/flask_jwt_extended/jwt_manager.py", line 93, in handle_expired_error
return self._expired_token_callback()
File "/Users/Danny/Documents/Codes/Ellington/NN App/website/application.py", line 43, in refresh
print('current_user:', current_user, decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNzVlNWExMy1mNjRiLTQxNmItOTY0ZC0wMDg5ODI4NGY2NGQiLCJleHAiOjE1NDcwMTk5ODUsImlhdCI6MTU0NzAxOTkyNSwidHlwZSI6InJlZnJlc2giLCJuYmYiOjE1NDcwMTk5MjUsImlkZW50aXR5IjoiZGFubnlAbWUuY29tIn0.LVEj6As2Uh_xgTbjm94b0M6mJeD0YLkf9KpgNKTZJOw'))
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jwt.py", line 92, in decode
jwt, key=key, algorithms=algorithms, options=options, **kwargs
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 156, in decode
key, algorithms)
File "/Users/Danny/.virtualenvs/Raghav_NN_WebApp/lib/python2.7/site-packages/jwt/api_jws.py", line 223, in _verify_signature
raise InvalidSignatureError('Signature verification failed')
InvalidSignatureError: Signature verification failed
在刷新函数上使用这两个独立的装饰器不会按照您想要的方式工作。过期的加载器装饰器不会设置当前用户,因为调用该回调函数时 jwt 无效。
而是尝试将刷新代码分解为两个装饰器独立使用的辅助函数:
def refresh_token(username):
# return flask response from here
@jwt.expired_token_loader
def handle_expired_token():
# get username here from raw jwt
username = 'todo'
return refresh_token(username)
@app.route(‘/refresh)
@jwt_refresh_token_required
def refresh_endpoint():
username = get_current_identity()
return refresh_token(username)
您也可以使用自定义装饰器代替 jwt_required 装饰器并实现类似的效果。这里讨论了一些例子:https://gitter.im/flask-jwt-extended/Lobby?at=5c1a9b37c35a3002474ddf3d