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

.NET 5 Web API 同时集成Identity与Microsoft AD身份认证的方案咨询

.NET Core Web API 混合JWT与Microsoft AD身份验证实现方案

1. 服务端中间件配置

首先安装必要的NuGet包:

  • Microsoft.AspNetCore.Authentication.JwtBearer(你现有JWT验证已用到)
  • Microsoft.AspNetCore.Authentication.MicrosoftAccount(Microsoft AD OAuth2验证支持)
    然后在Program.cs中配置多身份验证方案:
builder.Services.AddAuthentication(options =>
{
    // 默认使用JWT作为身份验证方案
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    // 你原有JWT验证配置,保持不变
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
    // 禁止未授权请求自动跳转登录页,适配API场景
    options.Events = new JwtBearerEvents
    {
        OnChallenge = context =>
        {
            context.HandleResponse();
            context.Response.StatusCode = 401;
            return Task.CompletedTask;
        }
    };
})
.AddMicrosoftAccount(options =>
{
    // 从Azure AD应用注册页面获取以下参数
    options.ClientId = builder.Configuration["AzureAd:ClientId"];
    options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
    // 回调路径要和Azure AD中配置的重定向URI保持一致
    options.CallbackPath = "/signin-microsoft";
    // 可选:额外请求的权限范围
    options.Scope.Add("user.read");
});
// 配置授权策略,支持两种验证方式
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("MixedAuth", policy =>
    {
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme, MicrosoftAccountDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
    });
});
// 中间件注册顺序不要调整
app.UseAuthentication();
app.UseAuthorization();

需要受保护的控制器或接口直接标注[Authorize(Policy = "MixedAuth")]即可同时支持两种验证方式。

2. 前端对接与接口参数说明

前后端分离场景下,推荐采用以下流程对接Microsoft登录:

  1. 前端构造Microsoft OAuth2授权地址,引导用户跳转登录,授权地址可直接参考Azure AD规则拼接,也可请求后端的挑战接口自动生成跳转地址
  2. 用户在Microsoft登录页完成验证后,会自动跳转回你预先配置的回调地址,URL会携带code(授权码)和可选的state参数
  3. 前端提取code参数,传给后端的Microsoft登录接口即可
    后端Microsoft登录接口仅需接收两个参数:
  • 必填:string code,Microsoft返回的授权码
  • 可选:string state,用于防跨站请求伪造校验

3. 控制器登录逻辑实现

Microsoft登录接口逻辑如下,完全复用你现有JWT签发和Identity用户操作逻辑:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IConfiguration _configuration;
    public AuthController(UserManager<IdentityUser> userManager, IConfiguration configuration)
    {
        _userManager = userManager;
        _configuration = configuration;
    }
    [HttpPost("microsoft-login")]
    public async Task<IActionResult> MicrosoftLogin([FromQuery] string code, [FromQuery] string? state = null)
    {
        // 用授权码向Microsoft换取用户身份信息
        var authenticateResult = await HttpContext.AuthenticateAsync(MicrosoftAccountDefaults.AuthenticationScheme);
        if (!authenticateResult.Succeeded)
        {
            return Unauthorized("Microsoft 身份验证失败");
        }
        var claimsPrincipal = authenticateResult.Principal;
        // 获取Microsoft用户唯一标识与基础信息
        var microsoftUserId = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
        var userEmail = claimsPrincipal.FindFirstValue(ClaimTypes.Email);
        var userName = claimsPrincipal.FindFirstValue(ClaimTypes.Name);
        // 检查用户是否已存在于Identity体系中
        var existingUser = await _userManager.FindByLoginAsync(MicrosoftAccountDefaults.AuthenticationScheme, microsoftUserId);
        if (existingUser == null)
        {
            // 首次登录,创建新Identity用户
            existingUser = new IdentityUser
            {
                UserName = userName,
                Email = userEmail,
                EmailConfirmed = true
            };
            var createResult = await _userManager.CreateAsync(existingUser);
            if (!createResult.Succeeded)
            {
                return BadRequest("用户创建失败");
            }
            // 关联Microsoft登录信息到Identity用户
            await _userManager.AddLoginAsync(existingUser, new UserLoginInfo(
                MicrosoftAccountDefaults.AuthenticationScheme,
                microsoftUserId,
                "Microsoft Account"
            ));
        }
        // 复用你现有JWT签发逻辑,生成令牌返回给前端
        var jwtToken = GenerateJwtToken(existingUser);
        return Ok(new { Token = jwtToken });
    }
    // 你原有JWT生成方法,保持不变
    private string GenerateJwtToken(IdentityUser user)
    {
        // 原有逻辑实现
    }
    // 你原有常规用户名密码登录接口,保持不变
    [HttpPost("normal-login")]
    public async Task<IActionResult> NormalLogin([FromBody] LoginRequest request)
    {
        // 原有逻辑实现
    }
}

4. 注意事项

  • Azure AD应用注册时,必须配置正确的重定向URI,与AddMicrosoftAccount中配置的CallbackPath保持一致
  • 两种登录方式最终返回的JWT格式完全一致,前端无需做额外适配
  • 若需要限制AD登录的租户范围,可在AddMicrosoftAccount配置中指定TenantId参数,避免外部租户用户登录

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 03:36:03