PHP使用$_SERVER变量重定向无法返回原请求页问题
问题根因
回跳逻辑失效的核心原因是PHP普通变量无法跨HTTP请求存活:
- 检测到无活跃会话时,你在当前请求的上下文里给
$redirect赋了原访问地址值,紧接着就发送Location头触发浏览器跳转到/confirm_auth.php?sso - 这个跳转是让浏览器发起全新的HTTP请求,新请求对应的PHP进程完全感知不到上一个请求里定义的普通变量
$redirect,因此isset($redirect)判断永远为false,自然会走到默认跳转首页的分支 - 代码存在额外隐患:发送
header跳转后没有加exit;终止脚本执行,后续逻辑会继续运行,可能引发不可预期的行为 - 最初贴出的代码里存在拼写错误:跳转域名写为
myinternalwebiste.net(website漏写字母b),如果线上代码存在该拼写错误也会直接导致跳转异常,补全的示例代码替换为example.net后不存在该问题。
修复方案
OneLogin SAML工具包本身支持通过RelayState参数自动传递登录后回跳地址,你只需要把原访问地址存储到可跨请求读取的介质中,在触发SAML登录时正确传入回跳地址即可,两种实现方案可任选:
方案1:通过Session暂存回跳地址(最稳妥)
你已经在代码开头调用了session_start(),直接把回跳地址存入$_SESSION数组,跳转到SSO入口页后再从Session读取即可,修改逻辑如下:
- 会话校验不通过的else块调整为:
else { if (isset($_SERVER['REQUEST_URI'])) { // 将回跳地址存入Session,不要使用普通变量存储 $_SESSION['login_redirect_url'] = "https://example.net" . $_SERVER['REQUEST_URI']; } header("Location: https://example.net/confirm_auth.php?sso"); exit; // 必须添加,终止后续代码执行 }
- sso参数处理逻辑调整为:
if (isset($_GET['sso'])) { // 优先从Session读取之前存储的回跳地址 if (isset($_SESSION['login_redirect_url'])) { $redirect = $_SESSION['login_redirect_url']; // 读取后立刻清除该Session字段,避免后续重复触发跳转 unset($_SESSION['login_redirect_url']); $auth->login($redirect); } else { $auth->login("https://example.net/"); } }
方案2:通过URL参数传递回跳地址
如果不想占用Session存储字段,也可以将原地址做URL编码后拼接在跳转链接的查询参数中传递,注意必须做URL编码,否则地址中携带特殊字符时会出现解析错误:
- else块跳转逻辑调整为:
else { $redirect_query = ""; if (isset($_SERVER['REQUEST_URI'])) { $target_url = "https://example.net" . $_SERVER['REQUEST_URI']; $redirect_query = "&redirect=" . urlencode($target_url); } header("Location: https://example.net/confirm_auth.php?sso" . $redirect_query); exit; }
- sso参数处理逻辑对应从
$_GET中读取回跳地址,注意必须做域名合法性校验,避免造成开放重定向漏洞:
if (isset($_GET['sso'])) { if (isset($_GET['redirect'])) { $redirect = $_GET['redirect']; // 仅允许跳转到本站点域名,防止被利用跳转至恶意站点 if (parse_url($redirect, PHP_URL_HOST) === "example.net") { $auth->login($redirect); } else { $auth->login("https://example.net/"); } } else { $auth->login("https://example.net/"); } }
补充说明
你在ACS回调逻辑中已经编写了RelayState的跳转处理,只要调用$auth->login()时传入的回跳地址正确,SAML工具包会自动将该地址写入RelayState参数,完成IdP认证回调后即可自动跳回用户最初访问的页面,不需要额外编写跳转逻辑。
内容的提问来源于stack exchange,提问作者Derek Ritchison
相关产品推荐
相关产品推荐

