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

API中Entra验证令牌的验证实现及相关问题咨询

API中Entra验证令牌的验证实现及相关问题咨询

嘿,我来帮你把Entra ID令牌验证的细节理清楚,还有你关心的几个问题一一解答~

关于API端点的访问策略:用属性装饰比匿名+手动验证更靠谱

首先明确:不建议把API端点设为匿名再手动验证,大部分主流框架(比如ASP.NET Core、Java Spring)都自带了适配Entra ID的认证中间件,用框架提供的属性装饰(比如ASP.NET里的[Authorize])会更安全、更省心——框架会自动帮你完成令牌的基础校验(签名、有效期、受众、发行方等),不用自己从零写校验逻辑,还能避免手动验证可能出现的漏洞。

当然如果是特殊场景(比如自定义权限逻辑),也可以设匿名后手动验证,但这是备选方案,优先用框架原生支持的方式。

如何验证令牌并获取用户信息

Entra ID颁发的是JWT格式的令牌,验证核心是要确保令牌是由你的Entra ID租户合法签发的,且目标受众是你的API。下面分两种场景给你示例代码:

场景1:用ASP.NET Core框架原生认证(推荐)

第一步,先安装依赖包:Microsoft.Identity.Web

然后在Program.cs里配置认证服务:

var builder = WebApplication.CreateBuilder(args);

// 添加Entra ID认证服务
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAd");

builder.Services.AddControllers();
// ...其他配置

var app = builder.Build();

// 启用认证中间件
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

对应的appsettings.json配置(替换成你的Entra ID信息):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "你的租户ID",
    "ClientId": "你的API应用注册的客户端ID"
  }
}

接下来在控制器里使用[Authorize]属性,就能自动拦截未携带合法令牌的请求,同时可以直接获取用户信息:

[ApiController]
[Route("api/[controller]")]
[Authorize] // 加上这个属性,只有携带合法Entra ID令牌的请求才能访问
public class ProtectedController : ControllerBase
{
    [HttpGet("user-info")]
    public IActionResult GetUserInfo()
    {
        // 从HttpContext.User里获取用户信息
        var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
        var userName = User.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value;
        var userEmail = User.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;

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

场景2:手动验证令牌(仅特殊场景使用)

如果必须设匿名端点,手动验证的话,可以用System.IdentityModel.Tokens.Jwt包来实现:

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;

public class TokenValidator
{
    private readonly IConfiguration _configuration;

    public TokenValidator(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task<ClaimsPrincipal?> ValidateTokenAsync(string token)
    {
        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = $"https://sts.windows.net/{_configuration["AzureAd:TenantId"]}/",
            ValidateAudience = true,
            ValidAudience = _configuration["AzureAd:ClientId"], // API的客户端ID
            ValidateLifetime = true,
            IssuerSigningKeyResolver = async (token, securityToken, kid, validationParameters) =>
            {
                // 从Entra ID获取签名密钥
                var keySetUrl = $"https://login.microsoftonline.com/{_configuration["AzureAd:TenantId"]}/discovery/v2.0/keys";
                var httpClient = new HttpClient();
                var response = await httpClient.GetAsync(keySetUrl);
                response.EnsureSuccessStatusCode();
                var keySet = await response.Content.ReadAsStringAsync();
                var keys = new JsonWebKeySet(keySet).GetSigningKeys();
                return keys;
            }
        };

        try
        {
            var handler = new JwtSecurityTokenHandler();
            var principal = handler.ValidateToken(token, validationParameters, out var validatedToken);
            return principal;
        }
        catch (Exception ex)
        {
            // 验证失败,记录日志或返回错误
            Console.WriteLine($"令牌验证失败:{ex.Message}");
            return null;
        }
    }
}

然后在匿名的Action里调用这个验证方法:

[ApiController]
[Route("api/[controller]")]
public class AnonymousController : ControllerBase
{
    private readonly TokenValidator _tokenValidator;

    public AnonymousController(TokenValidator tokenValidator)
    {
        _tokenValidator = tokenValidator;
    }

    [HttpGet("protected-anon")]
    public async Task<IActionResult> ProtectedAnon()
    {
        // 从请求头获取令牌
        if (!Request.Headers.TryGetValue("Authorization", out var authHeader))
        {
            return Unauthorized("未提供授权令牌");
        }

        var token = authHeader.ToString().Replace("Bearer ", "");
        var userPrincipal = await _tokenValidator.ValidateTokenAsync(token);

        if (userPrincipal == null)
        {
            return Unauthorized("令牌无效");
        }

        // 获取用户信息
        var userName = userPrincipal.FindFirst(ClaimTypes.Name)?.Value;
        return Ok(new { Message = $"已验证,欢迎{userName}!" });
    }
}

几个关键注意点

  • 确保你的API在Entra ID应用注册里已经暴露了API范围(比如api://{client-id}/access_as_user),并且客户端应用已经被授予了这个范围的权限。
  • 令牌的aud(受众)必须匹配API的客户端ID,否则验证会失败。
  • 生产环境中不要硬编码租户ID、客户端ID,用配置文件或环境变量管理。

备注:内容来源于stack exchange,提问作者vmasanas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 10:49:34