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
相关产品推荐
相关产品推荐

