You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask中查询参数传递失败问题排查与求助

登录后重定向丢失查询参数问题排查与优化建议

环境配置

  • 搭载自定义域名与Nginx的Flask应用(负责处理HTTPS流量)
  • 提供数据服务的FastAPI后端
  • 所有服务通过Docker Compose部署

业务逻辑概述

登录成功后,FastAPI返回授权令牌auth_token,Flask将该令牌作为查询参数重定向到/dashboard页面,后续请求携带令牌获取数据。

核心问题

登录重定向到/dashboard后,查询参数auth_token丢失,导致令牌值为None。曾尝试修改Nginx配置(如将proxy_pass改为http://python-flask:5000/$1$is_args$args)但未解决。


相关代码实现

Flask关键代码

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        # 转发请求到运行在8000端口的FastAPI应用
        try:
            user = request.form.get('user')
            password = request.form.get('password')
            # 请求FastAPI登录端点
            response = requests.post('http://python-fastapi:8000/login', params={'user': user, 'password': password})
            response_data = response.json()
            if response.status_code != 200:
                # 捕获HTTPException中的错误详情
                raise Exception(response_data["detail"])
            return redirect("/dashboard?auth_token="+response_data["token"])
        except Exception as e:
            return jsonify({"status": "error", "message": str(e)}), 500
    else:
        return render_template("login.html")
@app.route("/dashboard")
def dashboard():
    auth_token = request.get("auth_token")
    # 从FastAPI获取数据并返回
    try:
        response = requests.get('http://python-fastapi:8000/datasets', params={'auth_token': auth_token})
        if response.ok:
            data = response.json()
            return {"data": data}
        else:
            data = response.json()
            return {"error": data["detail"] + str(auth_token)}
    except Exception as e:
        return {"error": str(e)}

Nginx配置

server {
    listen 80;
    server_name automl.ddns.net;
    # HTTP请求强制重定向到HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name automl.ddns.net;
    resolver 127.0.0.11 valid=10s;
    resolver_timeout 5s;
    ssl_certificate /etc/letsencrypt/live/automl.ddns.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/automl.ddns.net/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # 非HTTPS请求重定向
    if ($scheme != "https") {
        return 301 https://$server_name$request_uri;
    }
    
    # 代理请求到Flask应用
    location ~ ^/(.*)$ {
        proxy_pass http://python-flask:5000/$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Docker Compose配置

version: '3.7'
services:
  mongo:
    image: mongo:latest
    container_name: mongodb
    restart: always
    ports:
      - "27017:27017"
    volumes:
      - ./data:/data/db
  python-fastapi:
    build:
      context: .
      dockerfile: DockerFile-fastapi
    container_name: python-fastapi
    restart: always
    command: python3 fastapi_client.py
    depends_on:
      - mongo
  python-flask:
    build:
      context: .
      dockerfile: DockerFile-flask
    container_name: python-flask
    restart: always
    command: python3 flask_app.py
    depends_on:
      - mongo
      - python-fastapi
      - nginx
  nginx:
    image: nginx:latest
    container_name: nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./myapp.conf:/etc/nginx/conf.d/myapp.conf
      - ./ssl:/etc/ssl
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
  mongo-express:
    image: mongo-express
    container_name: mongo-express
    restart: always
    ports:
      - "8081:8081"
    environment:
      ME_CONFIG_MONGODB_SERVER: mongodb
    depends_on:
      - mongo

FastAPI关键代码

# 登录后生成令牌的端点
@app.post("/login")
async def login(user: str, password: str):
    # 校验用户名格式
    if not user.isalnum():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Username must be alphanumeric")
    # 校验密码格式
    if not password.isalnum():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Password must be alphanumeric")
    # 校验用户名是否存在
    if not users_collection.find_one({"user": user}): 
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Username not found")
    # 校验密码是否正确
    if not users_collection.find_one({"user": user, "password": sha256(password.encode()).hexdigest()}): 
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Password is incorrect")
    # 结合时间戳生成令牌
    token = sha256((user + password+str(time.time())).encode()).hexdigest()
    # 保存会话到MongoDB
    session_collection.insert_one({"token": token, "user": user})
    return {"status": "ok", "message": "User logged in successfully", "token": token}
@app.get("/datasets")
async def datasets(auth_token: str):
    if auth_token is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="You need to provide an auth_token")
    session = session_collection.find_one({"token": auth_token})
    # 校验会话用户数量
    if len(session["user"]) != 1:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="There is more than one user in the session")
    for sess in session:
        user_id = sess["user"]
    datasets = datasets_collection.find({"user": user_id})
    # 返回数据集列表
    if len(datasets) == 0:
        return []
    result = []
    for dataset in datasets:
        ds = Dataset.from_mongo(dataset["dataset"])
        result.append({"id": str(dataset["_id"]), "name": ds.name})

问题排查与解决方案

1. Flask查询参数获取错误

Flask中获取URL查询参数的正确方法是request.args.get("auth_token"),而非request.get("auth_token")。后者并非Flask提供的查询参数获取接口,这是导致令牌为None的直接原因。

修改后的dashboard函数:

@app.route("/dashboard")
def dashboard():
    auth_token = request.args.get("auth_token")  # 修正参数获取方式
    # 后续逻辑保持不变

2. Nginx代理配置优化

当前Nginx的location ~ ^/(.*)$配合proxy_pass http://python-flask:5000/$request_uri会导致查询参数重复拼接($request_uri已包含完整路径和参数),建议简化配置:

location / {
    proxy_pass http://python-flask:5000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

当proxy_pass末尾不带斜杠时,Nginx会自动转发完整的请求URI(含查询参数)到后端,无需手动拼接。

3. Docker Compose依赖顺序调整

原配置中Flask依赖Nginx,但实际应该是Nginx依赖Flask,否则Flask启动时Nginx可能未就绪,导致请求转发异常:

python-flask:
    # 其他配置不变
    depends_on:
      - mongo
      - python-fastapi
nginx:
    # 其他配置不变
    depends_on:
      - python-flask

额外优化建议

1. 令牌传输安全优化

当前通过查询参数传输令牌存在泄露风险(会被记录在日志、浏览器历史中),建议改为:

  • 使用HTTP-only Cookie存储令牌,避免前端脚本访问
  • 或在请求头中携带令牌,格式如Authorization: Bearer <token>

2. 密码存储安全优化

当前直接存储SHA256哈希密码,建议添加随机盐提高破解难度:

# 用户注册时生成随机盐
import os
salt = os.urandom(16).hex()
hashed_password = sha256((password + salt).encode()).hexdigest()
# 存储用户名、盐、哈希密码
users_collection.insert_one({"user": user, "salt": salt, "password": hashed_password})

# 登录时验证密码
user_doc = users_collection.find_one({"user": user})
if user_doc and sha256((password + user_doc["salt"]).encode()).hexdigest() == user_doc["password"]:
    # 验证通过,生成令牌

3. FastAPI会话处理优化

  • 增加会话存在性校验,避免session为None时后续代码报错:
session = session_collection.find_one({"token": auth_token})
if not session:
    raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid auth token")
  • 修正用户ID获取逻辑,无需遍历会话字典,直接取值即可:
user_id = session["user"]

4. Docker服务健康检查

为各服务添加健康检查,确保服务就绪后再处理请求:

python-fastapi:
    # 其他配置不变
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

内容的提问来源于stack exchange,提问作者Antonio Gonzalez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:37:44