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

如何在MVC Web应用中调用授权Web API并动态获取存储Token

解决方法

实现逻辑说明

你当前的Web API已经配置了/token作为令牌签发端点,采用资源所有者密码授权模式,所以MVC应用只需在请求业务接口前,先向该端点发起请求获取动态Token即可,还可选择缓存Token减少重复请求开销。

前置准备

首先在MVC项目中定义Token响应接收模型,可放在Models文件夹下:

public class TokenResponse
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }
    [JsonProperty("token_type")]
    public string TokenType { get; set; }
    [JsonProperty("error")]
    public string Error { get; set; }
    [JsonProperty("error_description")]
    public string ErrorDescription { get; set; }
}

另外先确认Web API的Startup.cs中已添加OAuth中间件启用代码(你贴出的代码只定义了配置,漏了启用逻辑的话功能不生效):

// 追加到OAuth配置定义之后
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

修改MVC控制器代码

替换你写死Token的逻辑为动态获取逻辑:

if (opc.username == "test" && opc.password == "test")
{
    // ========== 动态获取Token逻辑开始 ==========
    string token = string.Empty;
    // 优先从缓存中取未过期的Token
    if (Session["AccessToken"] != null && (DateTime)Session["TokenExpireTime"] > DateTime.Now)
    {
        token = Session["AccessToken"].ToString();
    }
    else
    {
        using (var tokenClient = new HttpClient())
        {
            tokenClient.BaseAddress = new Uri("https://localhost:44304/");
            // 构造密码授权模式的请求参数
            var tokenRequestParams = new Dictionary<string, string>
            {
                {"grant_type", "password"},
                {"username", opc.username},
                {"password", opc.password}
            };
            var requestContent = new FormUrlEncodedContent(tokenRequestParams);
            // 请求Token端点
            var tokenResponse = await tokenClient.PostAsync("token", requestContent);
            if (tokenResponse.IsSuccessStatusCode)
            {
                var tokenResult = await tokenResponse.Content.ReadAsAsync<TokenResponse>();
                token = tokenResult.AccessToken;
                // 缓存Token,提前1分钟过期避免边界问题
                Session["AccessToken"] = token;
                Session["TokenExpireTime"] = DateTime.Now.AddSeconds(tokenResult.ExpiresIn - 60);
            }
            else
            {
                var errorResult = await tokenResponse.Content.ReadAsAsync<TokenResponse>();
                ViewBag.Error = $"获取Token失败:{errorResult.ErrorDescription}";
                return View("Error");
            }
        }
    }
    // ========== 动态获取Token逻辑结束 ==========

    IEnumerable<OperatorClass> OperatorObject = null;
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("https://localhost:44304/api/");
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

    var resultDisplay = await client.GetAsync("data");
    if (resultDisplay.IsSuccessStatusCode)
    {
        // 原代码用Wait()会同步阻塞,直接用await即可
        var readTable = await resultDisplay.Content.ReadAsAsync<IList<OperatorClass>>();
        OperatorObject = readTable;
        return View(OperatorObject);
    }
    else
    {
        OperatorObject = Enumerable.Empty<OperatorClass>();
        ModelState.AddModelError(String.Empty, "No records found");
        ViewBag.Error = "Token错误,可能已过期或无效,请检查Token服务";
        return View("Error");
    }
}
else
{
    ViewBag.Error = "用户名或密码错误!";
    return View("Login");
}

注意事项

  • 生产环境不要开启AllowInsecureHttp = true,必须使用HTTPS传输Token避免被窃取
  • 单实例部署可以用IMemoryCache替代Session存储Token,分布式部署可存入分布式缓存
  • 生产环境不要每次请求都new HttpClient,建议通过IHttpClientFactory注入复用实例,避免套接字耗尽问题
  • 不要硬编码接口地址、账号密码等配置,建议统一放在配置文件中读取

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 06:30:01