Azure AD OAuth2 POST方式SSO登录PHP会话丢失问题排查
我编写了基于Azure AD的OAuth2 POST方式SSO登录PHP代码,登录流程可正常运行,但重定向到welcome.php页面后,会话(session)变量全部为空。以下为参考代码:
$appid = "xxxxxxxxxxxx"; //(MASKING DATA FOR OBVIOUS REASONS) $tenantid = "xxxxxxxxxxxx"; //(MASKING DATA FOR OBVIOUS REASONS) $secret = "xxxxxxxxxxxx"; //(MASKING DATA FOR OBVIOUS REASONS) $login_url = "https://login.microsoftonline.com/" . $tenantid . "/oauth2/v2.0/authorize"; session_start(); $_SESSION['state'] = session_id(); echo "MS OAuth2.0 Demo "; if (isset($_SESSION['msatg'])) { echo "Authenticated " . $_SESSION["uname"] . " "; echo 'Log Out'; } else { echo 'You can Log In with Microsoft'; } if (isset($_GET['action']) && $_GET['action'] == 'login') { $params = array('client_id' => $appid, 'redirect_uri' => 'https://abc.xyz.com/sso/', 'response_type' => 'token', 'response_mode' => 'form_post', 'scope' => 'https://graph.microsoft.com/User.Read', 'state' => $_SESSION['state']); header('Location: ' . $login_url . '?' . http_build_query($params)); } if (array_key_exists('access_token', $_POST)) { $_SESSION['t'] = $_POST['access_token']; $t = $_SESSION['t']; $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $t, 'Content-type: application/json' )); curl_setopt($ch, CURLOPT_URL, "https://graph.microsoft.com/v1.0/me/"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $rez = json_decode(curl_exec($ch), 1); if (array_key_exists('error', $rez)) { var_dump($rez); die(); } else { $_SESSION['msatg'] = 1; //auth and verified $_SESSION['uname'] = $rez["displayName"]; $_SESSION['id'] = $rez["id"]; } curl_close($ch); header('Location: https://abc.xyz.com/sso/welcome.php'); } if (isset($_GET['action']) && $_GET['action'] == 'logout') { unset($_SESSION['msatg']); header('Location: https://abc.xyz.com/sso/'); }
当重定向到welcome.php页面时,我通过var_dump($_SESSION)得到的是空数组,希望会话数据能在该页面正常显示,请问我哪里出错了?
1. welcome.php未启动会话
PHP会话需要在每个使用$_SESSION的页面开头调用session_start(),如果welcome.php中缺少这行代码,就无法读取已存储的会话数据。
解决:
在welcome.php的最顶部(任何输出之前)添加:
session_start();
2. 会话Cookie配置不匹配
默认的会话Cookie路径、域名或安全设置可能和你的/sso/目录不兼容,导致重定向后Cookie无法被携带。
解决:
在登录页面的session_start()之前,显式配置会话Cookie参数:
session_set_cookie_params([ 'lifetime' => 0, 'path' => '/sso/', // 匹配你的SSO目录路径 'domain' => 'abc.xyz.com', // 不带http/https的域名 'secure' => true, // HTTPS环境必须开启 'httponly' => true, 'samesite' => 'Lax' // 适配POST回调的跨域场景 ]); session_start();
3. 重定向前未强制写入会话数据
调用header('Location: ...')时,会话数据可能还在内存中未写入服务器文件,导致重定向后数据丢失。
解决:
在重定向前添加session_write_close()强制写入,并执行exit终止后续代码:
// 在header()之前添加 session_write_close(); header('Location: https://abc.xyz.com/sso/welcome.php'); exit;
4. 缺失state参数验证(安全+数据一致性问题)
当前代码未验证Azure AD回调返回的state参数是否和之前存储的$_SESSION['state']一致,这不仅存在CSRF风险,也可能因会话不一致导致数据丢失。
解决:
处理POST回调时先验证state:
if (array_key_exists('access_token', $_POST)) { // 验证state参数 if (!isset($_POST['state']) || $_POST['state'] !== $_SESSION['state']) { die("Invalid state parameter"); } // 后续业务代码不变 }
5. 服务器会话存储权限问题
如果PHP会话存储目录(默认是/tmp)没有Web服务器用户(如www-data)的读写权限,会话数据无法持久化。
解决:
通过phpinfo()查看session.save_path的值,然后执行权限配置命令:
chown www-data:www-data /path/to/session/dir chmod 700 /path/to/session/dir
内容的提问来源于stack exchange,提问作者Chris Dsouza

