如何通过OAuth令牌实现ASP.NET Core MVC用户登录认证
ASP.NET Core MVC中传递自定义OAuth令牌到认证中间件的问题
问题背景
正在构建的ASP.NET Core MVC Web应用支持两种认证方式:
- 使用
.AddOpenIdConnect()的认证方式已正常运行 - 另一种需要从Web服务获取OAuth令牌进行认证
MVC控制器代码
// MVC public async Task<IActionResult> GetToken() { var tokenResponse = await _tokenService.GetToken(); // 尝试设置请求头,但JwtBearerEvents中未捕获到 string headerValue = new AuthenticationHeaderValue("Bearer", tokenResponse.AccessToken).ToString(); Request.Headers.Authorization = headerValue; return RedirectToAction("ShowToken", "Account"); } // 该Action指定使用JwtBearer认证方案 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] public IActionResult ShowToken() { return View(); }
认证配置代码
builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenHandlers.Add(new StrictTokenHandler()); options.TokenValidationParameters = tokenValidationParameters; options.SaveToken = true; options.Events = new JwtBearerEvents { OnMessageReceived = context => { var p = context.Properties; // 此时还未获取到令牌,无法赋值context.Token return Task.CompletedTask; }, OnChallenge = context => { var p = context.Properties; // 这里能拿到令牌,但JwtBearerChallengeContext没有可设置的Token属性 return Task.CompletedTask; }, OnTokenValidated = context => { return Task.CompletedTask; }, }; }) .AddCookie(options => { options.LoginPath = new PathString("/Account/Login"); }) .AddOpenIdConnect(options => { options.Authority = issuer; // 其余配置省略,此认证方式已正常运行 });
核心问题
- 从Web服务获取到OAuth令牌后,如何传递给身份认证中间件?(已尝试设置请求头,但未生效)
- 如何配置身份认证中间件来处理这些自定义获取的令牌?
补充:令牌服务返回的信息如下
解决方案
问题1:传递令牌到认证中间件
设置Request.Headers无效是因为重定向会创建新请求,原请求的Header不会被携带到跳转后的请求中。推荐两种可靠传递方式:
方式1:临时Cookie存储令牌
在GetToken方法中将令牌存入临时Cookie,有效期设为短时长避免泄露:
public async Task<IActionResult> GetToken() { var tokenResponse = await _tokenService.GetToken(); // 设置临时Cookie,生产环境启用Secure属性 Response.Cookies.Append("TempJwtToken", tokenResponse.AccessToken, new CookieOptions { HttpOnly = true, Secure = true, Expires = DateTimeOffset.UtcNow.AddMinutes(5), Path = "/" }); return RedirectToAction("ShowToken", "Account"); }
方式2:AuthenticationProperties传递
通过跳转时的属性传递令牌,适合短生命周期场景:
public async Task<IActionResult> GetToken() { var tokenResponse = await _tokenService.GetToken(); var properties = new AuthenticationProperties { Items = { ["TempToken"] = tokenResponse.AccessToken } }; return RedirectToAction("ShowToken", "Account", properties); }
问题2:配置中间件处理令牌
在JwtBearer的OnMessageReceived事件中读取存储的令牌,赋值给context.Token让中间件验证:
对应临时Cookie的配置
修改AddJwtBearer的事件逻辑:
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { // 保留原有配置 options.Events = new JwtBearerEvents { OnMessageReceived = context => { // 从临时Cookie读取令牌,验证后可删除 if (context.Request.Cookies.TryGetValue("TempJwtToken", out var token)) { context.Token = token; context.Response.Cookies.Delete("TempJwtToken"); } return Task.CompletedTask; }, // 保留其他事件配置 }; })
对应AuthenticationProperties的配置
如果用属性传递,在事件中读取:
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { // 保留原有配置 options.Events = new JwtBearerEvents { OnMessageReceived = context => { if (context.Properties.Items.TryGetValue("TempToken", out var token)) { context.Token = token; } return Task.CompletedTask; }, // 保留其他事件配置 }; })
额外优化建议
- 令牌验证通过后,建议调用
SignInAsync将身份转换为Cookie认证,后续请求无需重复处理JWT:// 可在OnTokenValidated事件或ShowToken Action中添加 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, User); - 生产环境务必启用HTTPS,确保令牌传输安全。
内容的提问来源于stack exchange,提问作者Mark Good
相关产品推荐
相关产品推荐


