基于OpenIDConnect/OAuth2与OpenIDDict,客户端安全调用API方案
客户端Web应用的OIDC认证Cookie是用来维持自身登录状态的,不能直接传递给API服务器做授权验证——Cookie和客户端域名绑定,跨域场景下浏览器会自动拦截,而且API服务器也无法解析客户端的Cookie(除非同域,但通常是独立服务)。正确做法是用Access Token来调用API。
客户端获取Access Token的两种常见方式
1. 授权码流+PKCE(推荐)
这是OIDC标准的安全流程,客户端完成认证时,除了获取用于身份认证的ID Token,还能拿到用于API授权的Access Token,以及用于刷新Token的Refresh Token。
配置客户端Web应用(以ASP.NET Core为例)
在Program.cs中配置OIDC时,确保请求API的权限范围(scope),并开启Token保存:
builder.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://your-auth-server.com"; // 认证服务器地址 options.ClientId = "your-client-id"; options.ClientSecret = "your-client-secret"; // 机密客户端需配置 options.ResponseType = "code"; options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("your-api-scope"); // 要访问的API权限范围 options.SaveTokens = true; // 关键:将Access Token保存到Cookie中 });
2. 隐式流(不推荐,仅适配纯前端SPA)
纯前端SPA可能会用隐式流直接获取Access Token,但安全性远低于授权码流+PKCE,目前已不推荐使用。
客户端调用API的实现方式
方式一:HttpClient自动携带Access Token
ASP.NET Core提供扩展可以让HttpClient自动从当前认证上下文获取Access Token并添加到请求头:
// 注册带Token处理的HttpClient builder.Services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri("https://your-api-server.com"); }) .AddHttpMessageHandler<BearerTokenHandler>(); // 注册Token处理类 builder.Services.AddTransient<BearerTokenHandler>();
BearerTokenHandler实现:
public class BearerTokenHandler : DelegatingHandler { private readonly IHttpContextAccessor _httpContextAccessor; public BearerTokenHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } return await base.SendAsync(request, cancellationToken); } }
在控制器/服务中注入调用:
private readonly IHttpClientFactory _httpClientFactory; public YourController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<IActionResult> CallApi() { var client = _httpClientFactory.CreateClient("ApiClient"); var response = await client.GetAsync("/api/your-endpoint"); response.EnsureSuccessStatusCode(); var data = await response.Content.ReadFromJsonAsync<YourModel>(); return View(data); }
方式二:手动获取Access Token调用API
不用HttpClient的话,也可以手动从HttpContext取Token,然后在请求头中添加Authorization: Bearer {token}:
public async Task<IActionResult> CallApiManually() { var accessToken = await HttpContext.GetTokenAsync("access_token"); using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var response = await client.GetAsync("https://your-api-server.com/api/your-endpoint"); // 处理响应逻辑 }
API服务器的验证配置
API服务器需要用OpenIDDict验证Access Token,确保请求合法:
builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = "https://your-auth-server.com"; options.Audience = "your-api-audience"; // 需和认证服务器配置的API受众一致 options.RequireHttpsMetadata = true; }); // 启用授权 builder.Services.AddAuthorization();
在API控制器/方法上添加授权特性:
[ApiController] [Route("api/[controller]")] [Authorize] public class YourController : ControllerBase { [HttpGet] public IActionResult Get() { return Ok(new { Message = "API访问成功" }); } }
你可能遗漏的关键点
- 混淆认证与授权载体:客户端Cookie是自身认证用的,API需要的是授权用的Access Token,两者作用完全不同。
- 未配置API权限范围:客户端认证时如果没请求API的scope,认证服务器不会颁发对应Access Token。
- 未开启SaveTokens:ASP.NET Core的OIDC配置中,
SaveTokens = true是将Access Token保存到Cookie的关键,否则后续无法获取Token。
内容的提问来源于stack exchange,提问作者Curtis
相关产品推荐
相关产品推荐

