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

强颜欢笑 提交于 2019-11-28 04:35:12
Sean Vieira

You can access request cookies through the request.cookies dictionary and set cookies by using either make_response or just storing the result of calling render_template in a variable and then calling set_cookie on the response object:

@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 lot of boilerplate in the home and do_that methods, all related to login. You can avoid that by writing your own decorator (see What is a decorator if you want to learn more about them):

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 home and do_that methods get much shorter:

# 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 need your cookie to have a particular name, I would recommend using flask.session as 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_required decorator even more:

# 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'])
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!