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

