首页 > 编程问答 >FastAPI Starlette Middleware 会话 - 重定向后无法访问会话数据

FastAPI Starlette Middleware 会话 - 重定向后无法访问会话数据

时间:2024-08-03 15:40:08浏览次数:15  
标签:python authentication session cookies fastapi

我正在使用 Fast API 作为后端编写一个简单的 Web 应用程序,并且我希望通过 Azure B2C 实现身份验证。这一切的逻辑现在并不重要,我只是想能够测试一下我是否可以使用不同的方法成功登录。

但是,在尝试了很长一段时间之后,我不明白,为什么我可以重定向后不会从会话中检索用户的数据。

import logging
import secrets

from fastapi import FastAPI, Request, HTTPException, status
from fastapi.responses import RedirectResponse, JSONResponse
from starlette.middleware.sessions import SessionMiddleware

from datetime import datetime
import jwt
import time
import json

from logger import logger
from env import *
from db_helpers import *
from mailer import *
from ad_helpers import *
from models import CreateUserRequest, InviteUserRequest, AcceptInvitationRequest, ProcessGoogleTokenRequest, ResendInvitationRequest, UserResponse, CreateUserResponse, InviteUserResponse

# Logging
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="your-very-secret-key", max_age=None)
logger.setLevel(logging.DEBUG)

logger.debug(f"B2C Authority: {B2C_AUTHORITY}")

# Middleware to handle session cookies
@app.middleware("http")
async def session_middleware(request: Request, call_next):
    response = await call_next(request)
    session = request.cookies.get('session')
    if session:
        response.set_cookie(key='session', value=session, httponly=True)
    return response

# FRONT ROUTES

@app.get('/login')
async def login(request: Request):
    """
    Get the authorization URL for login.
    """
    logger.debug("Executing login function")
    auth_url = build_auth_url()
    logger.debug(f"Authorization URL: {auth_url}")
    return JSONResponse(content={"auth_url": auth_url})

@app.get('/oauth_callback')
async def oauth_callback(request: Request):
    """
    Handle the OAuth2 authorization code and store user information in the session.
    """
    if 'code' in request.query_params:
        logger.debug("Authorization code received: %s", request.query_params['code'])
        cache = load_cache(request)
        try:
            result = build_msal_app(cache=cache).acquire_token_by_authorization_code(
                request.query_params['code'],
                scopes=[],
                redirect_uri=f"{BASE_URL}/oauth_callback")  # Ensure proper URL
            logger.debug(f"Token acquisition result: {result}")
            if 'error' in result:
                logger.error("Error in token acquisition: %s", result['error_description'])
                raise HTTPException(status_code=400, detail=f"Error: {result['error_description']}")
            user_info = result.get('id_token_claims')
            request.session['user'] = user_info
            save_cache(request, cache)
            logger.debug("Session data saved: %s", request.session['user'])
        except Exception as e:
            logger.error("Exception during token acquisition: %s", str(e))
            raise HTTPException(status_code=500, detail=str(e))
        return RedirectResponse("/user-profile")

    if not request.session.get('user'):
        logger.debug("No user session found, redirecting to login")
        return RedirectResponse("/login/")
    logger.debug("User session found, redirecting to profile")
    return RedirectResponse("/user-profile")

@app.get('/user-profile', response_model=UserResponse)
async def user_profile(request: Request):
    """
    Get the user's profile information from the session.
    """
    user = request.session.get('user')
    logger.debug("user data from session: %s", user)
    if not user:
        raise HTTPException(status_code=401, detail="User not logged in")

    return UserResponse(message="User profile retrieved successfully", email=user['emails'][0], id=user['oid'])

我将非常高兴您的帮助或任何有关我应该查看的内容的提示。

我尝试从 THEsession 中设置和检索用户的数据,但它显示 None每时每刻。


问题在于你试图从 request.cookies 中访问会话,而应该从 request.session 中访问。另外,你的 /oauth_callback 路由在重定向到 /user-profile 之前没有设置会话 cookie。

以下是如何修复代码:

import logging
import secrets

from fastapi import FastAPI, Request, HTTPException, status
from fastapi.responses import RedirectResponse, JSONResponse
from starlette.middleware.sessions import SessionMiddleware

# ... other imports

app = FastAPI()
app.add_middleware(
    SessionMiddleware, secret_key="your-very-secret-key", max_age=None
)
logger.setLevel(logging.DEBUG)

# ... other routes

@app.get('/oauth_callback')
async def oauth_callback(request: Request):
    """
    Handle the OAuth2 authorization code and store user information in the session.
    """
    if 'code' in request.query_params:
        logger.debug("Authorization code received: %s", request.query_params['code'])
        cache = load_cache(request)
        try:
            result = build_msal_app(cache=cache).acquire_token_by_authorization_code(
                request.query_params['code'],
                scopes=[],
                redirect_uri=f"{BASE_URL}/oauth_callback",  # Ensure proper URL
            )
            logger.debug(f"Token acquisition result: {result}")
            if 'error' in result:
                logger.error("Error in token acquisition: %s", result['error_description'])
                raise HTTPException(status_code=400, detail=f"Error: {result['error_description']}")
            user_info = result.get('id_token_claims')
            request.session['user'] = user_info
            save_cache(request, cache)
            logger.debug("Session data saved: %s", request.session['user'])

            # Set the session cookie in the response
            response = RedirectResponse("/user-profile")
            response.set_cookie(key="session", value=request.session.get("session"))
            return response

        except Exception as e:
            logger.error("Exception during token acquisition: %s", str(e))
            raise HTTPException(status_code=500, detail=str(e))

    if not request.session.get('user'):
        logger.debug("No user session found, redirecting to login")
        return RedirectResponse("/login/")
    logger.debug("User session found, redirecting to profile")
    return RedirectResponse("/user-profile")

@app.get('/user-profile', response_model=UserResponse)
async def user_profile(request: Request):
    """
    Get the user's profile information from the session.
    """
    user = request.session.get('user')
    logger.debug("user data from session: %s", user)
    if not user:
        raise HTTPException(status_code=401, detail="User not logged in")

    return UserResponse(message="User profile retrieved successfully", email=user['emails'][0], id=user['oid'])

解释:

  1. request.session 获取会话数据: /user-profile 路由中,我们使用 request.session.get('user') 获取用户数据,而不是 request.cookies.get('session')

  2. 在重定向之前设置会话 cookie: /oauth_callback 路由中,我们在重定向到 /user-profile 之前 使用 response.set_cookie() 设置会话 cookie。这确保了在重定向后,浏览器会将会话 cookie 发送回服务器,从而允许我们访问会话数据。

通过这些更改,你现在应该能够在重定向到 /user-profile 后从会话中成功检索用户数据。

标签:python,authentication,session,cookies,fastapi
From: 78822600

相关文章

  • 如何使用 BeautifulSoup python 查找选择标签的选定选项值
    我正在尝试从python中的htmlselect标签获取选定的值。好吧,当选项属性设置为selected="selected"时,它是成功的,但我试图废弃的网站具有不同的选项属性,例如:-html="""<select><optionvalue="">Pleaseselectavlalue</option><o......
  • 12:Python元组属性
    #元组tuple,元素不可被修改,不能被增加或则删除tu=(111,'alex',(11,22),[(33,44)],True,33,44,)#一般写元组的时候,最后可以加个逗号不报错print(tu)tu=(111,'alex',(11,22),[(33,44)],True,33,44,)v=tu[0]#元组索引print(v)tu=(111,'alex',(11,2......
  • FastAPI oauth2 + jwt 延长每个请求的exp时间
    根据fastapi的示例,我们可以使用ouath2和json网络令牌为用户创建登录:fromdatetimeimportdatetime,timedelta,timezonefromtypingimportAnnotatedfromfastapiimportDepends,FastAPI,HTTPException,statusfromfastapi.securityimportOAuth2PasswordBearer,O......
  • 如何在venv python中安装requirements.txt
    我是Python虚拟环境的初学者,在安装requirements.txt文件时遇到问题。问题是,当我运行命令来安装requirements.txt文件时,没有安装任何内容。平台:WindowsVS代码镜像如何解决这个问题?没有正确激活虚拟环境。请按照以下步骤操作:1.激活虚拟环境:在VSC......
  • 【代码随想录】图论复习(Python版)
    深度优先搜索1.搜索过程一个方向搜,不到黄河不回头,直到遇到绝境了,搜不下去了,再换方向(换方向的过程就涉及到了回溯)2.代码框架回溯法的代码框架:defbacktracking(参数):if终止条件:存放结果returnfor选择本层集合中的元素(树中节点孩子的数量......
  • 【Python】数据类型之字符串
    本篇文章将继续讲解字符串其他功能:1、求字符串长度功能:len(str)  ,该功能是求字符串str的长度。代码演示:2、通过索引获取字符串的字符。功能:str[a]  str为字符串,a为整型。该功能是获取字符串str索引为a处的字符。注意:字符串的索引是从0开始的。代码演示:注意......
  • 【Python】python基础
    本篇文章将讲解以下知识点:(1)循环语句(2)字符串格式化(3)运算符一:循环语句循环语句有两种:while   for本篇文章只讲解while循环格式:while 条件:  代码(只有条件为真的时候,此代码才会被执行,此处的代码可以是多行代码)(1)循环语句基本使用示例1:此处代码执行过程:1<3......
  • python 爬虫入门实战——爬取维基百科“百科全书”词条页面内链
    1.简述本次爬取维基百科“百科全书”词条页面内链,仅发送一次请求,获取一个html页面,同时不包含应对反爬虫的知识,仅包含最基础的网页爬取、数据清洗、存储为csv文件。爬取网址url为“https://zh.wikipedia.org/wiki/百科全书”,爬取内容为该页面所有内链及内链标识(下图蓝......
  • Python:match()和search()的区别
    在Python中,match()和search()函数通常与正则表达式(regularexpressions)一起使用,特别是在re模块中。尽管它们都用于搜索字符串中的模式,但它们在搜索行为上有关键的区别。re.match()re.match()函数尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()......
  • Python:range()函数的用法
    range()函数是Python中一个内置函数,用于生成一个数字序列。这个函数通常用于在for循环中迭代一个指定的次数。range()函数可以接收一到三个参数,分别是起始值(start)、结束值(stop)和步长(step),但步长是可选的,默认值为1。基本用法两个参数:range(start,stop)生成一个从star......