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

ASP.NET Core Web应用中消费JWT Token的配置及存储问题咨询

ASP.NET Core Web应用消费JWT Token解决方案

一、Token存储方案

优先选择加密HttpOnly Cookie存储JWT Token,该方案可避免XSS攻击窃取Token,安全性远高于前端localStorage/sessionStorage,适配传统MVC、Razor Pages架构的Web应用。

配置Cookie认证服务

在Program.cs中添加如下配置:

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

// 注册Cookie认证服务
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login"; // 未授权时跳转的登录页路径
        options.Cookie.HttpOnly = true; // 禁止前端JS读取Cookie,防XSS
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // 生产环境强制HTTPS传输
        options.ExpireTimeSpan = TimeSpan.FromHours(2); // 过期时间和JWT Token过期时间对齐
        options.SlidingExpiration = true; // 活跃用户自动续期
    });

// 其他服务注册...
builder.Services.AddControllersWithViews();
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

// 中间件顺序必须严格遵循:静态文件 -> 路由 -> 认证 -> 授权 -> 端点映射
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication(); // 必须放在UseAuthorization之前
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

登录逻辑实现

用户提交账号密码后调用JWT认证服务接口获取Token,再将Token写入认证Cookie,示例代码如下:

public class AccountController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public AccountController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginModel model)
    {
        if (!ModelState.IsValid) return View(model);

        // 调用JWTAuthentication服务端登录接口获取Token
        var authClient = _httpClientFactory.CreateClient();
        var response = await authClient.PostAsJsonAsync("JWT认证服务登录接口地址", new 
        {
            model.Username,
            model.Password
        });

        if (!response.IsSuccessStatusCode)
        {
            ModelState.AddModelError("", "账号或密码错误");
            return View(model);
        }

        var tokenResult = await response.Content.ReadFromJsonAsync<TokenResponse>();
        var accessToken = tokenResult.AccessToken;

        // 把Token存入Claims,后续请求可直接读取
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, model.Username),
            new Claim("AccessToken", accessToken)
        };

        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, 
            new ClaimsPrincipal(claimsIdentity));

        return RedirectToAction("Index", "Home");
    }
}

// 自定义类用于接收Token返回结果
public class TokenResponse
{
    public string AccessToken { get; set; }
    public int ExpiresIn { get; set; }
}

public class LoginModel
{
    [Required]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
}

二、API请求自动携带Token配置

通过自定义委托处理程序,实现所有发往受保护API的请求自动添加Authorization头,无需每个请求手动编写逻辑。

自定义请求委托处理程序

public class JwtTokenHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public JwtTokenHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 从当前登录用户的Claims中读取AccessToken
        var accessToken = _httpContextAccessor.HttpContext?.User.Claims
            .FirstOrDefault(c => c.Type == "AccessToken")?.Value;

        if (!string.IsNullOrEmpty(accessToken))
        {
            // 把Token加到请求头
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

注册服务并配置HttpClient

在Program.cs中添加如下配置:

// 注册自定义JWT请求处理程序
builder.Services.AddTransient<JwtTokenHandler>();

// 配置访问受保护API的HttpClient,自动应用JWT头
builder.Services.AddHttpClient("ProtectedApiClient", client =>
{
    client.BaseAddress = new Uri("AnyAPI服务根地址");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
}).AddHttpMessageHandler<JwtTokenHandler>(); // 挂载自定义处理程序

API调用示例

无需手动处理Token,直接使用配置好的HttpClient即可:

public class HomeController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public HomeController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> Index()
    {
        // 直接用配置好的HttpClient调用API,自动携带JWT Token
        var apiClient = _httpClientFactory.CreateClient("ProtectedApiClient");
        var data = await apiClient.GetFromJsonAsync<List<YourDataModel>>("目标API接口路径");
        return View(data);
    }
}

注意事项

  • 如果Web应用是Blazor WASM架构,可选择将Token存储在localStorage,配合AuthorizationMessageHandler实现自动携带请求头
  • Cookie的过期时间需要和JWT服务返回的Token过期时间保持一致,避免出现Cookie有效但Token已过期的问题
  • 生产环境必须启用Cookie的SecurePolicy,仅允许HTTPS传输Cookie,避免中间人攻击窃取信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:48:01