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

后端 未结 1 565
执笔经年
执笔经年 2020-12-07 17:54

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 rem

相关标签:
1条回答
  • 2020-12-07 18:25

    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'])
    
    0 讨论(0)
提交回复
热议问题