从刷新令牌创建非新鲜访问令牌时,flask-jwt-extended current_user身份=无

时间:2019-01-09 09:09:58

标签: authentication cookies flask jwt flask-jwt-extended

在我的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)中提供的相同代码来执行此操作,并且能够成功生成新的访问令牌。但是,在生成新的访问令牌后,访问令牌的身份声明等于“无”(get_raw_jwt())。无论何时我打印jwt_claims或尝试使用current_user = get_jwt_identity()来抓住当前用户时,对刷新令牌或访问令牌做什么,它都会将标识返回为None。对我来说,了解哪个用户向神经网络提交查询很重要,这样我就可以正确跟踪哪些用户提交了哪些查询(一对多关系)。

我尝试对refresh_token进行故障排除(解码),但遇到一个单独的问题:当我尝试使用decode_token()来解码refresh_token时,我得到了一个长回溯,以InvalidSignatureError结尾:签名验证失败。我拿了refresh_token并通过https://jwt.io运行,它解码了令牌。我可以在解码的令牌中看到,“身份”声明为我提供了用户的身份,但是它告诉我该令牌未经验证。但是,一旦我在屏幕上选中秘密的base64编码框,签名就会被验证,并且jwt的签名部分也会随之改变。我尝试对https://jwt.io为我提供了encode_token函数的修改后的jwt进行解码,但它仍然为我提供了相同的错误:InvalidSignatureError:签名验证失败。

我已经花了数小时阅读Google提供的有关flask-jet-extended和PyJWT的所有内容,但我不知道该如何解决。我试图将我的JWT_SECRET_KEY配置修改为不同的字符串,甚至使用base64对其进行编码,但这些都不能解决问题。我已打开和关闭JWT_COOKIE_CSRF_PROTECT配置。我已经打开和关闭了JWT_ACCESS_COOKIE_PATH和JWT_REFRESH_COOKIE_PATH。我尝试对refresh_token_cookie和CSRF_refresh_token cookie进行解码。我尝试在提供csrf_value = request.cookies.get('csrftoken')参数的同时运行带有refresh_token_cookie和CSRF_refresh_token cookie的解码_token()。我试过直接从jwt使用解码()函数( 从jwt导入解码)。

我只是不知道该怎么办,也找不到任何其他在线资源。任何帮助深表感谢!

我的下一步是将身份验证系统转移到flask-jet-simple或PyJWT。我真的很想使用JWT来验证我的用户。我不知道如何将JWT与flask-login结合使用,或者甚至不可能。我无法在网上找到任何有人通过JWT使用flask-login登录的资源。我确实找到了一个最近的仓库,称为flask-jwt-login,如果无法解决,可以尝试使用。最终,我想继续使用flask-jwt-extended。我需要专注于此网络应用程序的其他部分,并希望使他的工作变得更加公正。

无论如何,这是我的代码,工作流从/ login页面开始。这会将您重定向到/ NN页面。访问令牌过期后,如果您尝试重新加载/ NN页面,它将自动重新路由到/ token / refresh页面。刷新令牌后,它将返回到/ NN页面。

如果需要上传其他文件,请告诉我。

P.S。这是我关于堆栈溢出的第一篇文章,因此请原谅我所有格式问题。

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()返回的内容。

    {'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}

如您所见,身份声明等于无。

这是我看到的回溯:

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

1 个答案:

答案 0 :(得分:0)

在刷新功能上具有这两个单独的装饰器将无法按您希望的方式工作。过期的加载器装饰器将不会设置当前用户,因为调用该回调函数时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