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

如何将IdentityServer4身份映射至.NET MVC/.NET Core等Web应用

Great question! Let's walk through a clear, production-ready architecture for mapping your centralized AD/IdentityServer4 (IDSR4) identity across your web apps, plus refine your current setup to make identity reuse seamless.

分离式SSO架构示例

This setup keeps components decoupled while ensuring consistent identity across all apps:

  • IdentityServer4 Server: Centralized identity provider that authenticates users against AD, issues tokens, and manages client/app permissions
  • Web Apps (ASP.NET MVC/ABP): Relying parties that use OpenID Connect to redirect users to IDSR4 for login, then map the returned identity to their local session
  • Protected APIs: Resource servers that validate JWT tokens from IDSR4 to authorize requests and access user identity claims

Core Flow

  1. User accesses a protected route in your web app → redirected to IDSR4 login page
  2. User enters AD credentials → IDSR4 validates against AD
  3. IDSR4 issues id_token (for user identity) and access_token (for API access), redirects back to the web app
  4. Web app validates tokens, creates a local cookie session, and syncs AD identity to its local user store (if needed)
  5. Web app uses the access_token to call protected APIs, which validate the token and retrieve user claims directly

Identity Mapping Implementation

1. Web App (ASP.NET MVC/ABP) Setup

Your current OpenID Connect configuration is on the right track—here's how to refine it for reliable identity mapping:

Refined OpenID Connect Configuration

public void Configuration(IAppBuilder app)
{
    app.UseAbp();
    
    // Clear default claim filters to preserve all AD claims from IDSR4
    JwtSecurityTokenHandler.DefaultInboundClaimFilter.Clear();
    AntiForgeryConfig.UniqueClaimTypeIdentifier = JwtClaimTypes.Subject;

    app.UseCookieAuthentication(new CookieAuthenticationOptions 
    { 
        AuthenticationType = "Cookies" 
    });

    var openIdConfig = new OpenIdConnectAuthenticationOptions
    {
        Authority = "http://localhost:5443",
        ClientId = "demo",
        ClientSecret = "password",
        ResponseType = "id_token token", // Request both identity and access tokens
        SignInAsAuthenticationType = "Cookies",
        RedirectUri = "http://localhost:6234/",
        PostLogoutRedirectUri = "http://localhost:6234",
        Scope = "openid profile api1", // Request profile claims + API access
        RequireHttpsMetadata = false,

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            SecurityTokenValidated = async n =>
            {
                // Fetch full user claims from IDSR4's userinfo endpoint
                var userInfoClient = new UserInfoClient(
                    new Uri(n.Options.Authority + "/connect/userinfo"),
                    n.ProtocolMessage.AccessToken);
                var userInfo = await userInfoClient.GetAsync();

                // Create a new identity with AD-mapped claim types
                var nid = new ClaimsIdentity(
                    n.AuthenticationTicket.Identity.AuthenticationType,
                    ClaimTypes.Name, // Map AD display name to standard Name claim
                    ClaimTypes.Role);

                // Add all AD claims from userinfo
                nid.AddClaims(userInfo.Claims.Select(c => new Claim(c.Type, c.Value)));

                // Add critical token metadata for future use
                nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
                nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
                nid.AddClaim(new Claim("expires_at", 
                    DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

                // Update the authentication ticket with the new identity
                n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);

                // Sync AD identity to your ABP local user store
                await SyncAbpUserAsync(nid);
            }
        }
    };

    app.UseOpenIdConnectAuthentication(openIdConfig);
    app.UseExternalSignInCookie("Cookies");
    app.MapSignalR();
}

// Helper to sync AD user to ABP's local user table (no local password management)
private async Task SyncAbpUserAsync(ClaimsIdentity identity)
{
    var samAccountName = identity.FindFirst("sam_account_name")?.Value;
    var displayName = identity.FindFirst(ClaimTypes.Name)?.Value;
    var email = identity.FindFirst(ClaimTypes.Email)?.Value;

    if (string.IsNullOrEmpty(samAccountName)) return;

    // Check if user already exists in ABP's store
    var existingUser = await UserManager.FindByNameAsync(samAccountName);
    if (existingUser == null)
    {
        // Create new user with random password (users never log in locally)
        var newUser = new AbpUser
        {
            UserName = samAccountName,
            Name = displayName,
            EmailAddress = email,
            IsActive = true
        };
        await UserManager.CreateAsync(newUser, Guid.NewGuid().ToString());
    }
    else
    {
        // Update existing user with latest AD info
        existingUser.Name = displayName;
        existingUser.EmailAddress = email;
        await UserManager.UpdateAsync(existingUser);
    }
}

2. Protected API Setup

For APIs, skip cookie sessions and validate JWT tokens directly from IDSR4:

// ASP.NET Core API ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Configure JWT authentication against IDSR4
    services.AddAuthentication("Bearer")
        .AddJwtBearer("Bearer", options =>
        {
            options.Authority = "http://localhost:5443";
            options.RequireHttpsMetadata = false;
            options.Audience = "api1"; // Match IDSR4's ApiResource name
        });

    // Add authorization policy for API access
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ApiAccess", policy =>
        {
            policy.RequireAuthenticatedUser();
            policy.RequireClaim("scope", "api1");
        });
    });
}

// Configure middleware
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ... other middleware (exception handling, static files)
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

// Example API controller to access user identity
[ApiController]
[Route("api/[controller]")]
[Authorize("ApiAccess")]
public class UserController : ControllerBase
{
    [HttpGet("me")]
    public IActionResult GetCurrentUser()
    {
        // Retrieve AD claims directly from the validated token
        var userId = User.FindFirst("sam_account_name")?.Value;
        var userName = User.FindFirst(ClaimTypes.Name)?.Value;
        var email = User.FindFirst(ClaimTypes.Email)?.Value;

        return Ok(new { UserId = userId, UserName = userName, Email = email });
    }
}

IDSR4 Configuration Refinements

Your current IDSR4 setup works for testing—here's how to make it production-ready and improve claim management:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddIdentityServer()
        // Replace dev signing credential with a real certificate for production
        .AddSigningCredential(new X509Certificate2("path/to/your/cert.pfx", "cert-password"))
        .AddInMemoryIdentityResources(InMemoryInitConfig.GetIdentityResources())
        .AddInMemoryApiResources(InMemoryInitConfig.GetApiResources())
        .AddInMemoryClients(InMemoryInitConfig.GetClients())
        .AddLdapUsers<OpenLdapAppUser>(Configuration.GetSection("IdentityServerLdap"), UserStore.InMemory)
        // Add custom profile service to include AD claims in tokens/userinfo
        .AddProfileService<LdapProfileService>();
}

// Custom profile service to expose AD attributes as claims
public class LdapProfileService : IProfileService
{
    private readonly ILdapUserStore<OpenLdapAppUser> _userStore;

    public LdapProfileService(ILdapUserStore<OpenLdapAppUser> userStore)
    {
        _userStore = userStore;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var subjectId = context.Subject.GetSubjectId();
        var user = await _userStore.FindByIdAsync(subjectId);
        
        if (user == null) throw new ArgumentException("User not found in AD");

        // Map AD attributes to standard and custom claims
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.DisplayName),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim("sam_account_name", user.SamAccountName),
            // Add AD group roles as role claims (adjust based on your AD structure)
            new Claim(ClaimTypes.Role, "AppUser")
        };

        context.IssuedClaims.AddRange(claims);
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var subjectId = context.Subject.GetSubjectId();
        var user = await _userStore.FindByIdAsync(subjectId);
        context.IsActive = user != null && user.IsActive;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:37:32