检查用于user_id的Flask cookie并获取User(如果存在)或重定向(如果不存在)

时间:2017-04-18 13:43:18

标签: python flask

我所追求的是类似于@login_required装饰器所完成的东西,但我不确定自定义装饰器是否允许我将用户选项传递回我的路线功能。我的应用程序中有几个页面要求用户登录才能访问它们,因此我正在寻找最有效的方法/最少的代码复制到每个访问受限的路由中,以验证他们的cookie中是否有user_id(已记录) in),使用user_id缓存其User对象的get / query,并继续使用route函数,否则如果user_id不存在则重定向到登录页面。

我希望做的是:

@noteBP.route('/note', methods=['GET', 'POST'])
def new_note():

user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
...

该函数将检查会话cookie中的user_id并发送回User对象或重定向到登录页面:

def return_user_details_if_logged_in_else_redirect_to_login_url(next=None):
  user_id = session.get('user_id')
  if user_id:
    user_details = user_util.get_user_details_by_id(user_id)
    return user_details
  else:
    return redirect(url_for('usersBP.login', next=next))

事实证明,重定向与Abort的工作方式不同,即使你在另一个函数内部也被调用,所以现在我在路由函数中做了额外的处理来检查:

user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
if not user_details:
      return redirect(redirect_url)

我希望避免将这一段代码粘贴到每个访问受限路由的顶部。有没有更有效的方法/ DRY方法来做到这一点?如果使用装饰器,我如何将user_details导入路径功能?

1 个答案:

答案 0 :(得分:2)

如果要在视图内部调用的函数中重定向,请引发RequestRedirect。如果要在装饰器中重定向,请检查用户是否未登录并返回重定向而不是实际视图(或使用上一个函数来引发重定向)。

import functools
from flask import url_for, redirect, g
from werkzeug.routing import RequestRedirect

def require_login():
    if g.user is None:
        raise RequestRedirect(url_for('login'))

def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        require_login()

        # or
        # if g.user is None:
        #     return redirect(url_for('login'))

        return view(**kwargs)

    return wrapped_view

@app.route('/secret2')
@login_required
def secret1():
    return 'secret 1'

@app.route('/secret2')
def secret2():
    require_login()
    return 'secret 2'

g.user处理程序中填充before_request

from flask import session

@app.before_request
def load_user():
    g.user = None

    if 'user_id' in session:
        # use whatever caching logic you want here.
        g.user = User.query.get(session['user_id'])

在登录视图中填充session['user_id']

@app.route('/login')
def login():
    if request.method == 'POST':
        user = User.query.filter_by(username=request.form['username']).first()

        if user and user.check_password(request.form['password']:
            session['user_id'] = user.id
            return redirect(url_for('index'))

    return render_template('login.html')

现在,您可以从任何路径访问g.user,而无需明确传递。如果您确实要明确传递,请修改login_required

def require_login():
    if g.user is None:
        raise RequestRedirect(url_for('login'))

    return g.user

def login_required(view):
    @functools.wraps
    def wrapped_view(**kwargs):
        user = require_login()
        return view(user, **kwargs)

    return wrapped_view

@app.route('/secret')
def secret(user):
    return 'user {} is logged in'.format(user.id)

除了传递用户之外,所有这些都是Flask-Login的一部分,您应该重新考虑使用Flask-Login而不是尝试维护自己的解决方案。