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

如何为.NET API端点同时实现JWT与Azure AD双认证授权?

让.NET API同时支持自定义JWT和Azure AD两种授权方案

我来帮你搞定这个问题——要让你的.NET后端API同时支持用户名密码生成的自定义JWT和Azure AD登录两种授权方式,核心是在ASP.NET Core中配置多个认证方案,并让API端点接受任意一种有效的令牌。下面是完整的实现步骤:

一、核心原理

ASP.NET Core的认证系统支持同时注册多种认证方案(比如JwtBearer、AzureADBearer),你可以:

  • 设置全局默认的认证/挑战方案
  • 为单个API端点指定允许的认证方案
  • 自定义授权策略,要求满足任意一种认证方式

二、具体实现步骤

1. 安装必要的NuGet包

首先需要添加Azure AD认证相关的官方包,推荐使用Microsoft.Identity.Web(它封装了Azure AD认证的细节,更易用):

Install-Package Microsoft.Identity.Web

2. 修改Program.cs配置

下面是更新后的完整代码,我标注了关键改动点:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddCors(p => p.AddPolicy("corsapp", builder =>
{
    builder.WithOrigins("http://localhost:8082").AllowAnyMethod().AllowAnyHeader();
}));

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.PropertyNamingPolicy = null;
});

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "BaseWebApi", Version = "v1" });

    // 1. 添加自定义JWT的认证定义
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "自定义JWT授权格式: Bearer {token}",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    // 2. 添加Azure AD的OAuth2认证定义
    c.AddSecurityDefinition("AzureAD", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            AuthorizationCode = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri($"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/oauth2/v2.0/authorize"),
                TokenUrl = new Uri($"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/oauth2/v2.0/token"),
                Scopes = new Dictionary<string, string>
                {
                    { builder.Configuration["AzureAd:Scope"], "API访问权限" }
                }
            }
        }
    });

    // 3. 添加安全要求,允许任意一种认证方式
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
            },
            new string[] {}
        },
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "AzureAD" }
            },
            new[] { builder.Configuration["AzureAd:Scope"] }
        }
    });
});

// 配置多种认证方案
builder.Services.AddAuthentication(options =>
{
    // 可选:设置默认的认证方案,如果不设置,需要在端点上指定[Authorize(AuthenticationSchemes = "...")]
    // options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    // options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// 1. 保留原来的自定义JWT认证(指定方案名称为"CustomJwt")
.AddJwtBearer("CustomJwt", options =>
{
    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"]))
    };
})
// 2. 添加Azure AD认证方案(指定方案名称为"AzureAD")
.AddAzureADBearer("AzureAD", options =>
{
    builder.Configuration.Bind("AzureAd", options);
});

// 可选:创建授权策略,允许任意一种认证方案通过
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AllowEitherAuth", policy =>
    {
        policy.AuthenticationSchemes.Add("CustomJwt");
        policy.AuthenticationSchemes.Add("AzureAD");
        policy.RequireAuthenticatedUser();
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "DemoJWTToken v1");
        // 配置Swagger UI支持Azure AD登录
        c.OAuthClientId(builder.Configuration["AzureAd:ClientId"]);
        c.OAuthScopeSeparator(" ");
        c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
    });
}

app.UseHttpsRedirection();
app.UseCors("corsapp");

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

3. 配置appsettings.json

需要在配置文件中添加Azure AD的相关参数:

{
  "Jwt": {
    "Issuer": "你的自定义JWT发行者",
    "Audience": "你的自定义JWT受众",
    "Key": "你的自定义JWT密钥"
  },
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "你的Azure AD租户ID",
    "ClientId": "你的API在Azure AD中的应用ID",
    "Scope": "api://<你的API应用ID>/access_as_user" // 替换为你的API的访问范围
  }
}

4. 保护API端点的方式

你有两种灵活的方式来保护API:

方式一:使用全局策略(推荐)

在控制器或方法上应用自定义策略,这样端点会自动接受两种认证方式的令牌:

[ApiController]
[Route("[controller]")]
[Authorize(Policy = "AllowEitherAuth")]
public class WeatherForecastController : ControllerBase
{
    // ... 你的API方法逻辑
}

方式二:直接指定允许的认证方案

在[Authorize]属性中明确列出支持的方案:

[Authorize(AuthenticationSchemes = "CustomJwt,AzureAD")]
public IActionResult GetUserData()
{
    return Ok(HttpContext.User.Identity.Name);
}

三、测试验证

  1. 自定义JWT测试:通过用户名密码登录获取JWT,在请求头中添加Authorization: Bearer <你的自定义JWT>,调用API验证是否能正常访问。
  2. Azure AD测试:在Swagger UI中选择"AzureAD"认证,完成登录后获取令牌调用API;或者在React应用中通过Azure AD登录获取访问令牌,添加到请求头后调用API。

关键注意事项

  • 确保Azure AD中已正确注册API应用,并配置了正确的访问范围和客户端权限。
  • 两种认证方案的令牌验证逻辑是独立的,各自校验自身令牌的有效性。
  • 如果需要区分用户的登录方式,可以在控制器中通过HttpContext.User.Identity.AuthenticationType来判断(值为"CustomJwt"或"AzureAD")。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 16:40:50