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

如何在.NET Framework 4.7 ASP.NET Web API单个端点应用Microsoft Identity Platform认证

问题描述

我有一个基于.NET Framework 4.7.2构建的遗留ASP.NET Web API应用,目前未实现任何认证机制。我希望仅为该应用的一个端点而非整个应用配置Microsoft Identity Platform认证/授权。

我已创建另一个.NET 6.0 ASP.NET Core Web API应用,其Startup.ConfigureServices方法中包含以下代码:

JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddMicrosoftIdentityWebApi(_configuration);

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
     // The claim in the Jwt token where App roles are available.
     options.TokenValidationParameters.RoleClaimType = "roles";
});

该新应用运行正常,现在我尝试从该新应用向遗留应用发送已认证API请求,希望遗留端点处理认证,寻求正确实现方法。此前找到的相关问题未能解决我的需求。


解决方案

针对.NET Framework 4.7.2的Web API单个端点配置Microsoft Identity Platform JWT认证,按以下步骤操作:

1. 安装必要的NuGet包

在遗留项目中安装以下包:

Install-Package Microsoft.Owin.Security.Jwt
Install-Package System.IdentityModel.Tokens.Jwt
Install-Package Microsoft.IdentityModel.Protocols.OpenIdConnect

2. 为特定端点配置认证逻辑

方式一:Owin中间件路由匹配(推荐)

如果你的Web API基于Owin,可通过MapWhen仅对目标路由启用认证中间件,不影响其他端点:

using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Security.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Protocols;

[assembly: OwinStartup(typeof(YourLegacyApi.Startup))]
namespace YourLegacyApi
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 仅匹配目标端点路由
            app.MapWhen(context => 
                context.Request.Path.StartsWithSegments(new PathString("/api/protected-endpoint")), 
                subApp =>
            {
                var tenantId = "你的Azure AD租户ID";
                var apiClientId = "遗留API在Azure AD的应用ID";
                var authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";

                // 自动获取Azure AD的签名密钥
                var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                    $"{authority}/.well-known/openid-configuration",
                    new OpenIdConnectConfigurationRetriever(),
                    new HttpDocumentRetriever());

                subApp.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidIssuer = authority,
                        ValidateAudience = true,
                        ValidAudience = apiClientId,
                        ValidateLifetime = true,
                        RoleClaimType = "roles", // 和.NET 6应用保持一致
                        IssuerSigningKeyResolver = async (token, securityToken, kid, parameters) =>
                        {
                            var config = await configManager.GetConfigurationAsync(CancellationToken.None);
                            return config.SigningKeys.Where(k => k.KeyId == kid);
                        }
                    }
                });
            });

            // 保留原有Web API配置
            var apiConfig = new HttpConfiguration();
            WebApiConfig.Register(apiConfig);
            app.UseWebApi(apiConfig);
        }
    }
}

方式二:Action内手动验证Token(轻量场景)

如果不想配置Owin中间件,可直接在目标Action内完成Token验证:

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Protocols;

[RoutePrefix("api/protected")]
public class ProtectedController : ApiController
{
    private readonly IConfigurationManager<OpenIdConnectConfiguration> _configManager;
    private readonly string _authority = "https://login.microsoftonline.com/你的租户ID/v2.0";
    private readonly string _audience = "遗留API的Azure AD应用ID";

    public ProtectedController()
    {
        _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            $"{_authority}/.well-known/openid-configuration",
            new OpenIdConnectConfigurationRetriever(),
            new HttpDocumentRetriever());
    }

    [HttpGet]
    public async Task<IHttpActionResult> ProtectedEndpoint()
    {
        // 提取请求头中的Bearer Token
        var authHeader = Request.Headers.Authorization;
        if (authHeader == null || !authHeader.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase))
        {
            return Unauthorized();
        }

        var token = authHeader.Parameter;
        var config = await _configManager.GetConfigurationAsync(CancellationToken.None);

        var validationParams = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = _authority,
            ValidateAudience = true,
            ValidAudience = _audience,
            ValidateLifetime = true,
            RoleClaimType = "roles",
            IssuerSigningKeys = config.SigningKeys
        };

        try
        {
            var handler = new JwtSecurityTokenHandler();
            handler.ValidateToken(token, validationParams, out var validatedToken);
            
            // 验证通过,可从validatedToken中获取用户/角色信息
            var jwtToken = (JwtSecurityToken)validatedToken;
            var userRoles = jwtToken.Claims.Where(c => c.Type == "roles").Select(c => c.Value);

            return Ok("认证成功,已访问受保护端点");
        }
        catch (SecurityTokenException)
        {
            return Unauthorized();
        }
    }
}

3. 启用端点授权

在目标Action上添加[Authorize]特性,确保未认证请求被拦截:

[Authorize]
[HttpGet]
public IHttpActionResult ProtectedEndpoint()
{
    // ...
}

4. Azure AD配置要点

  • 为遗留API创建Azure AD应用注册,暴露API范围,并将该范围授权给.NET 6应用注册(需管理员同意)
  • 确保Token的aud字段匹配遗留API的应用ID或API URI
  • 如果需要角色权限,在遗留API的应用注册中添加应用角色,并分配给.NET 6应用或用户

5. 测试请求

从.NET 6应用发送请求时,需在请求头中携带:

Authorization: Bearer {从Microsoft Identity Platform获取的、针对遗留API的访问令牌}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:04:51