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

如何在ASP.NET Core MVC应用中调用JWT认证的ASP.NET Core Web API

在ASP.NET Core MVC中调用JWT认证的Web API

下面是一套完整的实现方案,替代手动在Postman传令牌的方式,让MVC应用自动处理JWT令牌的获取、携带与刷新:

1. 实现JWT令牌获取逻辑

首先,MVC应用需要从你的API令牌接口获取有效令牌(通常在用户登录时执行)。示例代码如下:

// 令牌响应模型,根据你的API返回结构调整
public class TokenResponse
{
    public string AccessToken { get; set; }
    public string TokenType { get; set; }
    public int ExpiresIn { get; set; }
    public string RefreshToken { get; set; } // 如果API支持刷新令牌
}

// 登录时获取令牌的方法(可封装到服务类中)
public async Task<TokenResponse> GetJwtTokenAsync(string username, string password)
{
    using var client = new HttpClient();
    var tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://your-api-domain/auth/token");
    
    // 按API要求的格式提交登录信息,通常是form-urlencoded
    var formData = new Dictionary<string, string>
    {
        ["grant_type"] = "password",
        ["username"] = username,
        ["password"] = password
    };
    tokenRequest.Content = new FormUrlEncodedContent(formData);
    
    var response = await client.SendAsync(tokenRequest);
    response.EnsureSuccessStatusCode(); // 处理请求失败的情况,比如用户名密码错误
    
    return await response.Content.ReadFromJsonAsync<TokenResponse>();
}

2. 存储令牌到Session

获取令牌后,将其存储到MVC的Session中,避免每次调用API都重新请求令牌:

// MVC登录控制器的Action
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (!ModelState.IsValid) return View(model);
    
    try
    {
        var tokenResponse = await GetJwtTokenAsync(model.Username, model.Password);
        // 将令牌存入Session
        HttpContext.Session.SetString("JwtAccessToken", tokenResponse.AccessToken);
        if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
        {
            HttpContext.Session.SetString("JwtRefreshToken", tokenResponse.RefreshToken);
        }
        return RedirectToAction("Index", "Home");
    }
    catch (HttpRequestException ex)
    {
        ModelState.AddModelError(string.Empty, "登录失败:" + ex.Message);
        return View(model);
    }
}

注意:要启用Session,需在Program.cs中添加配置:

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30); // 设置Session超时时间
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 在中间件管道中启用Session(放在UseRouting之后,UseAuthorization之前)
app.UseSession();

3. 配置HttpClient并自动携带令牌

使用IHttpClientFactory配置可复用的HttpClient,并通过消息处理器自动添加授权头,同时处理令牌过期刷新:

3.1 实现令牌刷新消息处理器

public class TokenRefreshHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        var accessToken = httpContext.Session.GetString("JwtAccessToken");
        
        // 为请求添加Bearer授权头
        if (!string.IsNullOrEmpty(accessToken))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        }

        var response = await base.SendAsync(request, cancellationToken);
        
        // 处理401未授权,尝试刷新令牌
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            var refreshToken = httpContext.Session.GetString("JwtRefreshToken");
            if (!string.IsNullOrEmpty(refreshToken))
            {
                var newTokenResponse = await RefreshTokenAsync(refreshToken);
                if (newTokenResponse != null && !string.IsNullOrEmpty(newTokenResponse.AccessToken))
                {
                    // 更新Session中的令牌
                    httpContext.Session.SetString("JwtAccessToken", newTokenResponse.AccessToken);
                    if (!string.IsNullOrEmpty(newTokenResponse.RefreshToken))
                    {
                        httpContext.Session.SetString("JwtRefreshToken", newTokenResponse.RefreshToken);
                    }
                    
                    // 重新设置授权头并重试请求
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newTokenResponse.AccessToken);
                    response = await base.SendAsync(request, cancellationToken);
                }
                else
                {
                    // 刷新失败,清除Session并跳转登录
                    httpContext.Session.Remove("JwtAccessToken");
                    httpContext.Session.Remove("JwtRefreshToken");
                }
            }
            else
            {
                // 无刷新令牌,直接跳转登录
                httpContext.Session.Remove("JwtAccessToken");
            }
        }

        return response;
    }

    private async Task<TokenResponse> RefreshTokenAsync(string refreshToken)
    {
        using var client = new HttpClient();
        var refreshRequest = new HttpRequestMessage(HttpMethod.Post, "https://your-api-domain/auth/refresh-token");
        
        var formData = new Dictionary<string, string>
        {
            ["grant_type"] = "refresh_token",
            ["refresh_token"] = refreshToken
        };
        refreshRequest.Content = new FormUrlEncodedContent(formData);
        
        var response = await client.SendAsync(refreshRequest);
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<TokenResponse>() : null;
    }
}

3.2 注册HttpClient与消息处理器

在Program.cs中注册相关服务:

builder.Services.AddHttpContextAccessor();
builder.Services.AddTransient<TokenRefreshHandler>();

// 注册带令牌处理的HttpClient
builder.Services.AddHttpClient("ApiClient", client =>
{
    client.BaseAddress = new Uri("https://your-api-domain/"); // 设置API基础地址
})
.AddHttpMessageHandler<TokenRefreshHandler>();

4. 在MVC控制器中调用API

注入IHttpClientFactory,创建HttpClient并调用API:

public class HomeController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

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

    public async Task<IActionResult> ApiData()
    {
        // 检查Session中是否有令牌,无则跳转登录
        if (string.IsNullOrEmpty(HttpContext.Session.GetString("JwtAccessToken")))
        {
            return RedirectToAction("Login", "Account");
        }

        var client = _httpClientFactory.CreateClient("ApiClient");
        var response = await client.GetAsync("api/products");
        
        // 处理请求结果,如果还是401,说明刷新也失败了,跳转登录
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            return RedirectToAction("Login", "Account");
        }
        
        response.EnsureSuccessStatusCode();
        var products = await response.Content.ReadFromJsonAsync<List<Product>>();
        
        return View(products);
    }
}

关键注意事项

  • 令牌存储:生产环境中如果需要更高安全性,可考虑将令牌加密后存储,或使用Cookie(设置HttpOnly、Secure属性)替代Session
  • 错误处理:需完善各种异常情况的捕获(如网络错误、API返回非200状态码)
  • 令牌过期时间:Session超时时间建议与API的AccessToken过期时间保持一致,或略长

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:17:54