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

在单个ASP.NET Core项目中切换SSO与独立用户账户认证方案

动态切换SSO与独立账户认证的实现方案

1. 同时注册两种认证方案

在Program.cs中同时配置ASP.NET Core Identity(独立账户)和Azure AD OpenID Connect(SSO),关键是禁用SSO方案的自动挑战,避免用户访问时直接重定向到Azure AD。

// 注册独立用户账户认证
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

// 注册Azure AD SSO认证,指定自定义方案名
builder.Services.AddAuthentication()
    .AddOpenIdConnect("AzureADSSO", options =>
    {
        options.ClientId = builder.Configuration["AzureAD:ClientId"];
        options.Authority = $"https://login.microsoftonline.com/{builder.Configuration["AzureAD:TenantId"]}/v2.0";
        options.CallbackPath = "/signin-oidc";
        options.SignedOutCallbackPath = "/signout-callback-oidc";
        // 禁用自动认证和挑战,防止默认重定向
        options.AutomaticAuthenticate = false;
        options.AutomaticChallenge = false;
    });

2. 实现基于组织状态的认证选择逻辑

创建登录入口,让用户提供组织标识(如域名、邮箱),后端查询SQL Server获取该组织的SSO启用状态,再引导到对应认证流程:

控制器核心代码

public class AccountController : Controller
{
    private readonly IOrganizationRepository _orgRepository;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public AccountController(IOrganizationRepository orgRepository, SignInManager<ApplicationUser> signInManager)
    {
        _orgRepository = orgRepository;
        _signInManager = signInManager;
    }

    // 登录入口页,让用户输入组织标识
    [HttpGet]
    public IActionResult Login() => View();

    [HttpPost]
    public async Task<IActionResult> SelectAuthScheme(string orgDomain)
    {
        var organization = await _orgRepository.GetByDomainAsync(orgDomain);
        if (organization == null)
        {
            ModelState.AddModelError("", "组织不存在");
            return View("Login");
        }

        if (organization.IsSsoEnabled)
        {
            // 触发Azure AD SSO认证
            return Challenge(new AuthenticationProperties { RedirectUri = "/" }, "AzureADSSO");
        }
        else
        {
            // 跳转到独立账户登录页
            return RedirectToAction("LocalLogin");
        }
    }

    // 独立账户登录逻辑
    [HttpGet]
    public IActionResult LocalLogin() => View();

    [HttpPost]
    public async Task<IActionResult> LocalLogin(LoginViewModel model)
    {
        if (!ModelState.IsValid) return View(model);

        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded) return RedirectToAction("Index", "Home");
        
        ModelState.AddModelError("", "登录失败,请检查账号密码");
        return View(model);
    }
}

3. 受保护资源的动态认证触发

如果用户直接访问受保护资源,需提前判断组织SSO状态,触发对应认证:

自定义中间件实现

public class DynamicAuthMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IOrganizationRepository _orgRepository;

    public DynamicAuthMiddleware(RequestDelegate next, IOrganizationRepository orgRepository)
    {
        _next = next;
        _orgRepository = orgRepository;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 仅处理未认证用户访问受保护资源的情况
        if (!context.User.Identity.IsAuthenticated && context.Request.Path.StartsWithSegments("/protected"))
        {
            // 根据系统设计获取组织标识(示例:从子域名提取)
            var orgDomain = context.Request.Host.Host.Split('.')[0];
            var organization = await _orgRepository.GetByDomainAsync(orgDomain);

            if (organization?.IsSsoEnabled == true)
            {
                await context.ChallengeAsync("AzureADSSO", new AuthenticationProperties { RedirectUri = context.Request.Path });
                return;
            }
            else
            {
                await context.ChallengeAsync(IdentityConstants.ApplicationScheme, new AuthenticationProperties { RedirectUri = context.Request.Path });
                return;
            }
        }

        await _next(context);
    }
}

// 在Program.cs中注册中间件(放在UseAuthorization之前)
app.UseMiddleware<DynamicAuthMiddleware>();
app.UseAuthorization();

4. 核心注意点

  • 必须禁用OpenID Connect的AutomaticAuthenticate和AutomaticChallenge,避免自动重定向
  • 组织标识的获取方式需匹配你的系统架构(如邮箱域名、子域名、组织ID参数等)
  • 确保两种认证方案完成后,用户身份上下文包含组织信息,方便后续权限判断

内容的提问来源于stack exchange,提问作者Avery Deemer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:54:51