web-dev-qa-db-fra.com

Python Flask: garder une trace des sessions utilisateur? Comment obtenir l'identifiant du cookie de session?

Je veux créer une webapp simple dans le cadre de mon activité d'apprentissage. Webapp est censé demander à l'utilisateur de saisir son email_id s'il rencontre un visiteur pour la première fois, il se souvient de l'utilisateur via un cookie et le connecte automatiquement pour exécuter les fonctions.

C'est la première fois que je crée une application Web basée sur l'utilisateur. J'ai une impression bleue en tête, mais je n'arrive pas à comprendre comment la mettre en œuvre. Je suis principalement confus quant à la manière de collecter les cookies utilisateur. J'ai examiné divers didacticiels et flask_login mais je pense que ce que je veux implémenter est beaucoup plus simple par rapport à ce que flask_login implémente.

J'ai également essayé d'utiliser flask.session mais c'était un peu difficile à comprendre et je me suis retrouvé avec une implémentation imparfaite.

Voici ce que j'ai jusqu'à présent (c'est rudimentaire et destiné à communiquer mon cas d'utilisation):

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'))
15
pg2455

Vous pouvez accéder aux cookies de demande via request.cookies dictionary et définissez les cookies à l'aide de make_response ou simplement stocker le résultat de l'appel de render_template dans une variable puis appelant set_cookie sur l'objet réponse :

@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'))

SÉCHAGE du code

Maintenant, vous remarquerez qu'il y a beaucoup de passe-partout dans les home et do_that méthodes, toutes liées à la connexion. Vous pouvez éviter cela en écrivant votre propre décorateur (voir Qu'est-ce qu'un décorateur si vous voulez en savoir plus à leur sujet):

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

Ensuite, votre home et do_that les méthodes deviennent beaucoup plus courtes:

# 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)

Utiliser ce qui est fourni

Si vous n'avez pas besoin de votre cookie pour avoir un nom particulier, je vous recommande d'utiliser flask.session car il contient déjà beaucoup de subtilités (il est signé de sorte qu'il ne peut pas être falsifié, peut être défini sur HTTP uniquement, etc.). Que DRYs notre login_required décorateur encore plus:

# 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'))

Et puis vos méthodes individuelles peuvent obtenir l'utilisateur via:

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