基于Microsoft OAuth2.0与Entra ID的PHP登录认证问题咨询
基于Microsoft OAuth2.0(Entra ID)的登录功能实现需求
我需要实现基于Microsoft OAuth2.0的登录功能,目前有三个PHP文件:
- Index.php会自动跳转到auth.php发起Microsoft登录流程
- 预期授权码会发送至success.php,用于获取Access Token
- 已使用Entra ID完成配置,敏感信息已替换为
XXXXXXXX - 本地账号登录功能正常,核心需求是解决Microsoft OAuth2.0与Entra ID的认证对接问题
Index.php
<?php //TODO: Azure Synchronisation session_start(); //* 检查用户是否已认证 if (isset($_SESSION['token'])) { header('Location: http://localhost/success.php'); //! 用户已认证 exit(); } else { header('Location: http://localhost/auth.php'); //! 跳转至Microsoft登录页 exit(); } //* 检查用户是否已通过Office服务登录 if (isset($_SERVER['AUTH_USER'])) { /*sleep(1); header('Location: http://localhost/success.php'); //! 用户已登录 exit();*/ } require_once($_SERVER['DOCUMENT_ROOT'].'/Assets/Configs/server.php'); //*引入server.php if ($_SERVER["REQUEST_METHOD"] == "POST") { $username = $_POST['uid']; $password = $_POST['pwd']; //* 建立数据库连接 $connectionInfo = array("UID" => $uid, "PWD" => $pwd, "Database" => $databaseName, "CharacterSet" => $CharacterSet); $conn = sqlsrv_connect($serverName, $connectionInfo); if ($conn) { $enteredUsername = $_POST['uid']; $enteredPassword = $_POST['pwd']; $hashedEnteredPassword = hash('sha256', $enteredPassword); //!密码哈希处理 $query = "SELECT * FROM Benutzer WHERE Benutzername=?"; $params = array($enteredUsername); $options = array("Scrollable" => SQLSRV_CURSOR_STATIC); $stmt = sqlsrv_query($conn, $query, $params, $options); if ($stmt === false) { die("<font color='red'><b>查询错误:</b></font> " . print_r(sqlsrv_errors(), true)); } $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC); if ($row) { if ($hashedEnteredPassword == $row['HashedPasswort']) { $_SESSION['user']=$row['Benutzername']; $message = "<font color='green'><b>登录成功!</b></font>"; echo "数据库中的哈希密码: " . $row['HashedPasswort']; header("Location: success.php"); exit(); } else { $message = "<font color='black'><b>用户名或密码错误,请重试</b></font>"; } } else { $message = "<font color='black'><b>用户名或密码错误,请重试</b></font>"; } sqlsrv_free_stmt($stmt); sqlsrv_close($conn); } else { die("<font color='red'><b>连接失败:</b></font> " . print_r(sqlsrv_errors(), true)); } } ?> <head> <title>ITH - Development Test</title> <meta charset="UTF-8"> <link rel="stylesheet" href="Css.css"> <script src="Js.js"></script> </head> <body> <header> <div id="Überschrift"> <h1>ITH Bolting Technology - Login</h1> </div> <h4>本页面用于登录 - This page is for Logging in</h4> <h4>请输入登录信息 - Please enter your Login-data</h4> <?php if(isset($message)) { echo $message; } ?> </header> <main> <div id="Anmeldung"> <form method="post" action="index.php"> <label for="uid"><b>用户名 - Username</b></label> <input type="text" placeholder="输入用户名" name="uid" required> <label for="pwd"><b>密码 - Password</b></label> <input type="password" placeholder="输入密码" name="pwd" required> <button type="submit" value="Login"> 登录 - Login </button> </form> </div> </main> </body> </html>
auth.php
<?php session_start(); // 检查用户是否已认证 if (isset($_SESSION['token'])) { header('Location: http://localhost/success.php'); exit(); } // 引入所需库 require_once __DIR__.'/vendor/autoload.php'; // Azure AD OAuth2提供者配置 $clientId = 'XXXXXXXXXXXXXXXXX'; $clientSecret = 'XXXXXXXXXXXXXXXXX'; $redirectUri = 'http://localhost/success.php'; // 根据您的配置修改 $tenantId = 'XXXXXXXXXXXXXXXXX'; $scope = 'https://graph.microsoft.com/User.Read https://graph.microsoft.com/profile https://graph.microsoft.com/email https://graph.microsoft.com/offline_access'; $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => $clientId, 'clientSecret' => $clientSecret, 'redirectUri' => $redirectUri, 'urlAuthorize' => "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize", 'urlAccessToken' => "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token", 'urlResourceOwnerDetails' => '', 'scopes' => $scope, // 根据需求修改权限范围 'enablePkce' => true ]); $authorizationUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2state'] = $provider->getState(); header('Location: ' . $authorizationUrl); exit(); ?>
success.php
<!DOCTYPE html> <html lang="DE"> <?php session_start(); $username = $_SESSION['user']; $Erfolg ="登录成功"; // 从URL中提取授权码 $authorization_code = isset($_GET['code']) ? $_GET['code'] : null; ?> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="Css2.css"> <script src="Js.js"></script> <title> 登录成功 </title> </head> <body> <header> <div id="Überschrift"> <h1>ITH Bolting Technology - Login</h1> <?php echo "用户: "; echo($username); ?> </div> </header> <main> <h3> <?php $Erfolg ="登录成功"; // Guzzle HTTP Client require_once __DIR__.'/vendor/autoload.php'; $clientId = 'XXXXXXXXXXXXXXXXX'; $clientSecret = 'XXXXXXXXXXXXXXXXX'; $redirectUri = 'http://localhost/success.php'; // 根据您的配置修改 $tenantId = 'XXXXXXXXXXXXXXXXX'; $scope = 'https://graph.microsoft.com/User.Read https://graph.microsoft.com/profile https://graph.microsoft.com/email https://graph.microsoft.com/offline_access'; $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => $clientId, 'clientSecret' => $clientSecret, 'redirectUri' => $redirectUri, 'urlAuthorize' => "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize", 'urlAccessToken' => "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token", 'urlResourceOwnerDetails' => '', 'scopes' => $scope // 根据需求修改权限范围 ]); // 从URL参数获取授权码 $authorizationCode = isset($_GET['code']) ? $_GET['code'] : null; if (!$authorizationCode) { exit('未提供授权码。'); } try { // 请求访问令牌 $accessToken = $provider->getAccessToken('authorization_code', ['code' => $authorizationCode]); // 获取访问令牌值 $accessTokenValue = $accessToken->getToken(); // 获取用户信息 $graph = new Microsoft\Graph\Graph(); $graph->setAccessToken($accessTokenValue); $user = $graph->createRequest('GET', '/me') ->setReturnType(Microsoft\Graph\Model\User::class) ->execute(); // 显示用户信息 echo '用户名: ' . $user->getDisplayName(); echo '邮箱: ' . $user->getMail(); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { exit('获取访问令牌失败: ' . $e->getMessage()); } // 检查会话中是否存在用户数量 /*if (isset($_SESSION['user_count'])) { echo "<p>Azure AD用户数量: " . $_SESSION['user_count'] . "</p>"; } else { echo "<p>未获取到用户信息。</p>"; }*/ ?> </h3> <div id="authCode"> <?php if ($authorization_code) { // 在页面显示授权码 echo "<script>document.addEventListener('DOMContentLoaded', function() { document.getElementById('authCode').textContent = '授权码: $authorization_code'; });</script>"; } ?> </div> </main> </body> </html>
核心问题修正建议
1. 补全CSRF状态验证
在success.php获取授权码后,必须验证state参数防止跨站请求伪造:
// 添加在获取$authorizationCode的代码之后 if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { unset($_SESSION['oauth2state']); exit('无效的state参数。'); }
2. 存储访问令牌到会话
获取到令牌后,将其存入会话以便后续接口调用使用:
// 在获取$accessToken后添加 $_SESSION['token'] = $accessTokenValue; $_SESSION['refresh_token'] = $accessToken->getRefreshToken(); // 存储刷新令牌用于续期
3. 检查Entra ID应用配置
确保Entra ID应用注册中:
- 重定向URI与代码中的
redirectUri完全一致(包含协议、域名、端口) - 已为应用授予
User.Read等所需权限,且完成管理员同意(租户级应用需此步骤) - 应用程序类型选择正确(Web应用选"Web",桌面应用选"公共客户端")
4. 优化错误捕获范围
在success.php中添加通用异常捕获,排查更多潜在问题:
// 原IdentityProviderException捕获后添加 catch (\Exception $e) { exit('请求出错: ' . $e->getMessage()); }
内容的提问来源于stack exchange,提问作者user21272320
相关产品推荐
相关产品推荐

