Python Flask:跟踪用户会话?如何获取会话 Cookie ID?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32640090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 11:57:53  来源:igfitidea点击:

Python Flask: keeping track of user sessions? How to get Session Cookie ID?

pythonsessionflaskflask-login

提问by pg2455

I want to build a simple webapp as part of my learning activity. Webapp is supposed to ask for user to input their email_id if it encounters a first time visitor else it remembers the user through cookie and automatically logs him/her in to carry out the functions.

我想构建一个简单的 webapp 作为我学习活动的一部分。如果 Webapp 遇到第一次访问者,它应该要求用户输入他们的 email_id,否则它会通过 cookie 记住用户并自动让他/她登录以执行这些功能。

This is my first time with creating a user based web app. I have a blue print in my mind but I am unable to figure out how to implement it. Primarily I am confused with respect to the way of collecting user cookie. I have looked into various tutorials and flask_login but I think what I want to implement is much simpler as compared to what flask_login is implementing.

这是我第一次创建基于用户的 Web 应用程序。我有一个蓝图,但我无法弄清楚如何实施它。主要是我对收集用户 cookie 的方式感到困惑。我查看了各种教程和flask_login,但我认为与flask_login 正在实现的相比,我想要实现的要简单得多。

I also tried using flask.sessionbut it was a bit difficult to understand and I ended up with a flawed implementation.

我也尝试过使用,flask.session但它有点难以理解,我最终得到了一个有缺陷的实现。

Here is what I have so far (it is rudimentary and meant to communicate my use case):

这是我到目前为止所拥有的(它是基本的,旨在传达我的用例):

from flask import render_template, request, redirect, url_for


@app.route("/", methods= ["GET"])
def first_page():
  cookie = response.headers['cookie']
  if database.lookup(cookie):
   user = database.get(cookie) # it returns user_email related to that cookie id 
  else:
    return redirect_url(url_for('login'))
  data = generateSomeData() # some function
  return redirect(url_for('do_that'), user_id, data, stats)

@app.route('/do_that', methods =['GET'])
def do_that(user_id):
  return render_template('interface.html', user_id, stats,data) # it uses Jinja template

@app.route('/submit', methods =["GET"])
def submit():
  # i want to get all the information here
  user_id = request.form['user_id']# some data
  answer = request.form['answer'] # some response to be recorded
  data = request.form['data'] # same data that I passed in do_that to keep 
  database.update(data,answer,user_id)
  return redirect(url_for('/do_that'))

@app.route('/login', methods=['GET'])
def login():
  return render_template('login.html')

@app.route('/loggedIn', methods =['GET'])
def loggedIn():
  cookie = response.headers['cookie']
  user_email = response.form['user_email']
  database.insert(cookie, user_email)
  return redirect(url_for('first_page'))

采纳答案by Sean Vieira

You can access request cookies through the request.cookiesdictionaryand set cookies by using either make_responseor just storing the result of calling render_templatein a variable and then calling set_cookieon the response object:

您可以通过request.cookies字典访问请求 cookie并通过使用make_response或仅将调用结果存储render_template在变量中然后调用set_cookie响应对象来设置 cookie :

@app.route("/")
def home():
    user_id = request.cookies.get('YourSessionCookie')
    if user_id:
        user = database.get(user_id)
        if user:
            # Success!
            return render_template('welcome.html', user=user)
        else:
            return redirect(url_for('login'))
    else:
        return redirect(url_for('login'))

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        # You should really validate that these fields
        # are provided, rather than displaying an ugly
        # error message, but for the sake of a simple
        # example we'll just assume they are provided

        user_name = request.form["name"]
        password = request.form["password"]
        user = db.find_by_name_and_password(user_name, password)

        if not user:
            # Again, throwing an error is not a user-friendly
            # way of handling this, but this is just an example
            raise ValueError("Invalid username or password supplied")

        # Note we don't *return* the response immediately
        response = redirect(url_for("do_that"))
        response.set_cookie('YourSessionCookie', user.id)
        return response

@app.route("/do-that")
def do_that():
    user_id = request.cookies.get('YourSessionCookie')
    if user_id:
        user = database.get(user_id)
        if user:
            # Success!
            return render_template('do_that.html', user=user)
        else:
            return redirect(url_for('login'))
    else:
        return redirect(url_for('login'))

DRYing up the code

干掉代码

Now, you'll note there is a lotof boilerplate in the homeand do_thatmethods, all related to login. You can avoid that by writing your own decorator (see What is a decoratorif you want to learn more about them):

现在,您会注意到和方法中有很多样板,都与登录有关。您可以通过编写自己的装饰器来避免这种情况(如果您想了解更多关于它们的信息,请参阅什么是装饰器):homedo_that

from functools import wraps
from flask import flash

def login_required(function_to_protect):
    @wraps(function_to_protect)
    def wrapper(*args, **kwargs):
        user_id = request.cookies.get('YourSessionCookie')
        if user_id:
            user = database.get(user_id)
            if user:
                # Success!
                return function_to_protect(*args, **kwargs)
            else:
                flash("Session exists, but user does not exist (anymore)")
                return redirect(url_for('login'))
        else:
            flash("Please log in")
            return redirect(url_for('login'))
    return wrapper

Then your homeand do_thatmethods get muchshorter:

然后你的homedo_that方法变得短:

# Note that login_required needs to come before app.route
# Because decorators are applied from closest to furthest
# and we don't want to route and then check login status

@app.route("/")
@login_required
def home():
    # For bonus points we *could* store the user
    # in a thread-local so we don't have to hit
    # the database again (and we get rid of *this* boilerplate too).
    user = database.get(request.cookies['YourSessionCookie'])
    return render_template('welcome.html', user=user)

@app.route("/do-that")
@login_required
def do_that():
    user = database.get(request.cookies['YourSessionCookie'])
    return render_template('welcome.html', user=user)

Using what's provided

使用所提供的

If you don't needyour cookie to have a particular name, I would recommend using flask.sessionas it already has a lot of niceties built into it (it's signed so it can't be tampered with, can be set to be HTTP only, etc.). That DRYs up our login_requireddecorator even more:

如果您不需要您的 cookie 具有特定名称,我建议您使用flask.session它,因为它已经内置了很多细节(它已签名,因此不能被篡改,可以设置为仅 HTTP 等) .) 这使我们的login_required装饰器更加干燥:

# You have to set the secret key for sessions to work
# Make sure you keep this secret
app.secret_key = 'something simple for now' 

from flask import flash, session

def login_required(function_to_protect):
    @wraps(function_to_protect)
    def wrapper(*args, **kwargs):
        user_id = session.get('user_id')
        if user_id:
            user = database.get(user_id)
            if user:
                # Success!
                return function_to_protect(*args, **kwargs)
            else:
                flash("Session exists, but user does not exist (anymore)")
                return redirect(url_for('login'))
        else:
            flash("Please log in")
            return redirect(url_for('login'))

And then your individual methods can get the user via:

然后您的个人方法可以通过以下方式获取用户:

user = database.get(session['user_id'])