PAYATHON 2026

如何通过第三方 .NET 应用中的按钮自动登录 PHP 网站?

技术老齐

结论

优先采用 OpenID Connect(OIDC)实现单点登录。用户在桌面软件中点击按钮,通过浏览器打开网站登录入口,由 PHP 网站完成身份认证,并为浏览器建立网站登录会话。

如果客户已有统一身份提供方(Identity Provider,简称 IdP),先确认它是否支持 OIDC 或 SAML,以及能否对接现有 LDAP 目录。PHP 网站即使没有使用框架,也可以接入这些协议。

桌面软件发送的 Email、Username 或姓名只能用于查找账户,不能作为登录凭据,因为这些字段无法证明请求者就是该用户。

点击后能否直接进入个人区域,取决于浏览器能否利用现有登录状态或企业集成认证完成认证。桌面软件已登录,不代表浏览器也已登录。

OAuth 2.0、OIDC 和 LDAP 各负责什么?

OAuth 2.0 主要处理授权,例如允许桌面软件调用某个用户的受保护 API。网站需要确认当前用户身份时,应使用建立在 OAuth 2.0 之上的 OIDC,它提供身份认证所需的协议和 ID Token。

LDAP 是目录访问协议。即使桌面软件从 LDAP 查到了真实姓名和邮箱,PHP 网站仍需核实这些信息的提供方是否可信,以及信息是否对应当前操作的用户。JSON 是数据格式,cURL 是请求工具,都无法提供这种身份保证。

可以采用以下流程:

.NET 桌面软件打开网站
        ↓
PHP 网站将浏览器重定向到 IdP
        ↓
IdP 完成认证,返回授权码
        ↓
PHP 网站在服务器端兑换并验证身份结果
        ↓
映射本地账户,建立 PHP Session
        ↓
浏览器进入个人区域

LDAP 可以由 IdP 在后端对接,目录访问和用户认证都交给 IdP 处理。

谁是 client,应该选择哪种流程?

参与方角色
PHP 网站OIDC Relying Party,同时也是 OAuth 2.0 client
统一身份提供方OpenID Provider,同时承担授权服务器角色
浏览器承载重定向和网站 Cookie
.NET 桌面软件打开网站入口;仅此用途时,无需参与 OAuth 流程

网站应使用 Authorization Code Flow,并配合 PKCE。PHP 网站能在服务器端保存凭据,还应按 IdP 的注册配置完成客户端认证。PKCE 和客户端认证各有用途。

传统的“四种流程”在这个场景中的适用范围是:

  • Authorization Code Flow 适合网站通过重定向完成登录。
  • Implicit Flow 不应作为新登录集成的选择。
  • Resource Owner Password Credentials Grant 不应用于让桌面软件收集并转交用户密码。
  • Client Credentials Grant 表示应用自身的身份,不能表示某个用户登录。

只有当桌面软件还需要独立调用受保护 API 时,才需要另行注册为 native client,使用 Authorization Code Flow + PKCE。分发到用户电脑上的程序无法可靠地保守内置的 client_secret,应按 public client 设计。

推荐的接入步骤

  1. 确认客户是否已有 IdP,以及桌面软件目前如何认证用户。仅知道使用了 LDAP,还无法确定浏览器如何实现单点登录。
  2. 在 IdP 中注册 PHP 网站,配置精确的 HTTPS 回调地址,例如 https://portal.example.com/sso/callback.php。
  3. 在 /sso/start.php 中使用 OIDC 库生成认证请求,将本次流程所需的 state、nonce 和 PKCE code_verifier 保存在服务端,再重定向浏览器。
  4. 收到回调后校验 state,使用授权码和 code_verifier 兑换令牌,再由库验证 ID Token 的签名、iss、aud、有效期、nonce 等内容。
  5. 使用经过验证的 iss 与 sub 组合定位外部身份,找到对应的网站账户,并检查账户是否启用、是否有访问权限。
  6. 更新 PHP Session ID,写入本地用户 ID,再重定向到个人区域。

解码 JWT 不等于验证其内容。必须完成签名、签发方和接收方等验证,应使用支持相应流程的 OIDC 库,避免自行实现令牌验证。

账户关联规则也要提前确定。邮箱可能变化,不同组织可能使用相同的用户名。已有账户首次绑定外部身份时,应要求用户验证原账户,或由管理员预先建立可信映射,避免按邮箱匹配后直接自动绑定。

代码示例

.NET:通过默认浏览器打开网站入口

在按钮的事件处理代码中执行:

using System.Diagnostics;

Process.Start(new ProcessStartInfo
{
    FileName = "https://portal.example.com/sso/start.php",
    UseShellExecute = true
});

打开固定的网站地址即可,无需传递用户名、密码或令牌。

网站随后发起 OIDC 登录。如果浏览器已有有效的 IdP 会话,且策略允许复用,用户可能无需再次输入凭据。首次登录、会话过期或需要多因素认证时,用户仍需完成相应交互。

PHP:认证完成后建立本地会话

以下代码只处理会话,不包含 OIDC 回调验证,不能单独作为完整的登录接口使用。

将会话配置放在网站共用的初始化代码中,在任何输出和 OIDC 流程处理之前执行:

<?php
ini_set('session.use_strict_mode', '1');
ini_set('session.use_only_cookies', '1');

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

session_start();

完成 OIDC 验证、本地账户映射和访问资格检查后,才能调用以下函数:

<?php
function completeWebsiteLogin(int $localUserId): void
{
    if ($localUserId <= 0) {
        throw new InvalidArgumentException('Invalid local user ID');
    }

    if (!session_regenerate_id(true)) {
        throw new RuntimeException('Failed to regenerate session ID');
    }

    $_SESSION = [
        'user_id' => $localUserId,
    ];

    header('Cache-Control: no-store');
    header('Location: /account/', true, 303);
    exit;
}

$localUserId 必须来自服务器端查找到的可信本地账户,不能直接使用 $_GET['user_id']、客户端提交的邮箱或未经验证的令牌字段。

示例中的 SameSite=Lax 适用于常见的跨站顶层 GET 回调。如果使用 response_mode=form_post 等跨站 POST 回调,需要重新评估 Cookie 配置,否则回调时可能无法取得保存 state 的会话。

桌面程序通过 HttpClient 请求登录接口,即使收到成功响应和 Cookie,这些 Cookie 通常也只保存在该 HTTP 客户端中。默认浏览器使用自己的 Cookie 存储,因此仍需让浏览器访问网站,接收网站设置的会话 Cookie。

没有统一 IdP,能否使用一次性登录票据?

可以考虑使用一次性登录票据,前提是双方有明确的信任关系,并单独完成安全设计。

例如,客户有可信后端,且后端已经完成用户认证;网站能够认证该后端,并验证它提供的用户身份。在这个前提下,双方可以实现短期、一次性登录票据:

  1. 客户后端根据已认证的用户申请票据,网站按事先建立的身份映射确定本地账户。
  2. 网站生成不可预测的随机票据,在服务器端保存其哈希、目标用户、有效期和使用状态。
  3. 桌面软件打开网站的票据兑换入口。
  4. 网站验证并原子性地消费票据,再建立浏览器会话。

有效期可以设为 60 秒,但这只是设计示例,具体时长应根据实际跳转耗时和风险确定。一次性消费必须由数据库事务、条件更新等机制保证,不能先查询,再无条件标记为已使用。

如果客户只有安装在用户电脑上的桌面程序,就不能把程序内置的共享密钥当作可信后端凭据,也不能允许程序提交任意用户名来换取登录票据。

还要防范登录 CSRF。攻击者可能将自己的登录链接发送给其他人,让对方的浏览器进入攻击者的账户。通常应先由浏览器建立登录事务,再将票据绑定到该事务;无法绑定时,需要设置明确的账户确认环节。短期有效和一次性消费本身无法解决这个问题。

这种方案可以满足特定合作系统的需求,但票据泄露、重放、账户绑定和会话安全都需要自行设计处理。已有统一身份系统时,优先沿用 OIDC 或 SAML。

上线前需要注意什么?

  • 全程使用 HTTPS,精确校验回调地址。登录后的目标地址使用固定路径或白名单,避免开放重定向。
  • 不要将密码、长期访问令牌、ID Token 或 PHP Session ID 放进 URL。一次性票据如果通过 URL 传递,也要控制日志记录、设置 Referrer-Policy,并尽快跳转到不含票据的地址。
  • 将 state、nonce 和 PKCE 参数绑定到同一次登录事务,并在使用后使其失效。
  • 外部认证通过后,网站仍需检查本地账户状态和权限。通过身份认证,不代表用户有权访问个人区域中的所有资源。
  • 明确浏览器已有账户与桌面账户不一致时如何处理,避免在用户不知情的情况下切换身份。
  • 分别设计网站退出、IdP 退出和桌面软件退出。关闭其中一方的会话,其他会话未必会自动结束。

备注:内容仅供参考。