Files
ephron-ren-prd/fixes/login-redirect-fix.md

3.7 KiB
Raw Blame History

ephron.ren 登录重定向失效修复方案

问题描述

在主页www.ephron.ren未登录状态下点击右上角「未登录」跳转至登录页输入账号密码点击登录后页面不会自动重定向回主页而是停留在登录页。

影响范围

所有子服务Home / Blog / Canvas / Prompt的登录后重定向均受影响。只要登录页 URL 中带有 redirect 参数指向其他子域名,登录后都无法跳转。

根因分析

直接原因

Chrome 浏览器的 CSPContent Security Policy引擎将登录表单的 POST 请求拦截了。

浏览器控制台报错:

Sending form data to 'https://auth.ephron.ren/api/login?redirect=https%3A//www.ephron.ren/'
violates the following Content Security Policy directive: "form-action 'self'".
The request has been blocked.

技术细节

站点响应头中设置了 CSP

form-action 'self'

该指令限制表单只能提交到同源 URL。

问题在于:登录表单将 redirect 参数拼接在 form action 的 query string 中:

<form method="POST" action="/api/login?redirect=https%3A//www.ephron.ren/">

Chrome 的 CSP 引擎在解析 form action URL 时,会将 query string 中的 %3A 解码为 :,使得 :// 看起来像协议标识符。引擎误判该 URL 包含跨域目标(https://www.ephron.ren/),从而拦截了表单提交。

验证证据

场景 form action 结果
不带 redirect 参数 /api/login 登录成功,跳转到 /login-success
带 redirect 到自身域名 /api/login?redirect=https%3A//auth.ephron.ren/admin 正常
带 redirect 到其他子域名 /api/login?redirect=https%3A//www.ephron.ren/ CSP 拦截

修复方案

redirect 参数从 URL query string 改为表单 hidden field使 form action 保持干净,不含任何可能触发 CSP 误判的字符。

修改文件 1auth/templates/login.html

-    <form class="login-form" method="POST" action="/api/login{% if redirect %}?redirect={{ redirect | urlencode }}{% endif %}">
+    <form class="login-form" method="POST" action="/api/login">

         {% if error %}
         <div class="alert alert-error">
             {{ error }}
         </div>
         {% endif %}

+        {% if redirect %}
+        <input type="hidden" name="redirect" value="{{ redirect }}">
+        {% endif %}
+
         <div class="form-group">
             <label for="username">用户名</label>

修改文件 2auth/src/routes/api.py

  async def login(
      request: Request,
      username: str = Form(...),
      password: str = Form(...),
-     redirect: str | None = Query(default=None),
-     return_url: str | None = Query(default=None, alias="return_url"),
-     next_url: str | None = Query(default=None, alias="next"),
+     redirect: str | None = Form(default=None),
+     return_url: str | None = Form(default=None),
+     next_url: str | None = Form(default=None),
  ):

修复原理

修改前 修改后
form action /api/login?redirect=https%3A//www.ephron.ren/ /api/login
redirect 传递方式 URL query string hidden form fieldbody
CSP 检查 action URL 含 // → 误判为跨域 → 拦截 action URL 为纯路径 → 同源 → 放行

不受影响的部分

  • 登录页 GET 请求传递 redirect 参数query string不受影响CSP form-action 只拦截 POST
  • 登录失败时重定向回登录页(携带 redirect 参数)不受影响,因为那是 303 跳转,不是表单提交
  • validate_redirect() 安全校验逻辑无需修改
  • cookie 设置逻辑无需修改