You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:10:55