17370845950

c# 如何实现单点登录 sso
SSO(单点登录)是跨系统认证协议协作机制,C#应用作为SP接入IdP(如Azure AD、IdentityServer6),而非自行实现;.NET 6+推荐OIDC+Microsoft.Identity.Web,需正确配置Cookie策略、HTTPS及时间同步。

什么是 SSO,C# 里不直接“实现”而是“接入”

SSO(单点登录)不是某个 C# 类库能一键开启的功能,而是一套跨系统认证协议的协作机制。你在 C# 应用中要做的,是作为 服务提供方(SP) 接入已有的身份提供方(IdP),比如 Azure AD、Okta、Auth0、或自建的 IdentityServer。硬写一套符合 SAML/OIDC 规范的 IdP 在生产环境极不推荐——协议细节多、安全要求高、密钥轮换、签名验证、时钟偏移处理等极易出错。

ASP.NET Core 中最常用的是 OIDC + Microsoft.Identity.Web

现代 C# Web 应用(.NET 6+)基本都走 OpenID Connect(OIDC)路线,配合 Microsoft.Identity.Web 包可快速接入 Azure AD 或其他兼容 OIDC 的 IdP。它封装了 token 获取、验证、用户上下文注入等逻辑,避免手写 OpenIdConnectEvents 处理 redirect_uri、nonce、state 等易错环节。

  • 安装 NuGet 包:Microsoft.Identity.WebMicrosoft.Identity.Web.UI
  • Program.cs 中注册服务:
    builder.Services.AddMicrosoftIdentityWebAppAuthenti

    cation(builder.Configuration, "AzureAd");
  • 配置 appsettings.json 中的 AzureAd 节点,至少包含:ClientIdClientSecret(或证书)、Instance(如 https://login.microsoftonline.com/)、TenantId
  • 控制器方法加 [Authorize] 即可触发自动重定向到 IdP 登录页

自己搭 IdP?用 IdentityServer6(非 .NET 自带)

如果必须自建 IdP(例如统一管理多个内部系统),不要用已停止维护的 IdentityServer4,改用 IdentityServer6(基于 .NET 6+,开源且持续更新)。它本身是独立服务,C# 客户端只需按 OIDC 标准接入——也就是说,你的业务系统仍是 SP,只是 IdP 换成了你自己的实例。

  • IdentityServer6 需单独部署(可托管在 Kestrel/IIS/Docker),暴露 /.well-known/openid-configuration 端点
  • 客户端应用仍用 Microsoft.Identity.Web,但 Instance 改为你的 IdP 地址,TenantId 改为 common 或具体租户 ID
  • 关键区别:你需要手动在 IdP 侧注册每个客户端(即你的 C# 应用),配置 ClientIdRedirectUrisPostLogoutRedirectUris、是否启用 PKCE 等
  • 切勿在 IdP 中硬编码用户密码——应对接现有目录(如 LDAP、SQL Server 用户表)或通过 IProfileService 扩展用户声明

常见坑:Cookie 同站策略、HTTPS 强制、时钟偏差

本地开发时最常卡在三类问题,和代码逻辑无关,纯属部署/配置层面:

  • SameSite=None + Secure=true:Chrome/Firefox 要求跨站 Cookie 必须同时设这两个属性,否则登录后跳回应用时 HttpContext.User 为空。ASP.NET Core 6+ 默认已适配,但若降级或自定义 Cookie 策略需检查
  • 所有回调地址(RedirectUri)必须用 HTTPS(生产环境),即使本地用 https://localhost:5001;HTTP 会被 IdP 拒绝
  • 服务器时间与 NTP 时间偏差超过 5 分钟会导致 JWT 签名验证失败(exp/nbf 检查),错误信息类似:System.IdentityModel.Tokens.Jwt.SecurityTokenInvalidLifetimeException
  • IdP 返回的 id_token 若含中文或特殊字符,某些老版本 System.Text.Json 会解析失败,建议显式指定 JsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping

真正难的从来不是“怎么写那几行代码”,而是理解谁发 token、谁验 token、谁存 session、谁管登出通知——这些角色划分错了,后面所有配置都会指向奇怪的 401 或循环重定向。