MVC .NET cookie 身份验证系统通过令牌身份验证访问 Web Api

Posted

技术标签:

【中文标题】MVC .NET cookie 身份验证系统通过令牌身份验证访问 Web Api【英文标题】:MVC .NET cookie authenticated system acessing a Web Api with token authentication 【发布时间】:2014-04-29 09:21:34 【问题描述】:

我有一个具有 Ownin cookie 身份验证的 Mvc 5 客户端。 我还有一个受 Owin Bearer 令牌保护的 Web Api(我使用了创建令牌端点的 VS2013 Web Api 模板)

好的,现在我的 Mvc 5 客户端需要使用我的 WebApi。 我创建了一个获取不记名令牌的方法:

internal async Task<string> GetBearerToken(string siteUrl, string Username, string Password)

     HttpClient client = new HttpClient();

     client.BaseAddress = new Uri(siteUrl);
     client.DefaultRequestHeaders.Accept.Clear();

     HttpContent requestContent = new StringContent("grant_type=password&username=" + Username + "&password=" + Password, Encoding.UTF8, "application/x-www-form-urlencoded");

     HttpResponseMessage responseMessage = await client.PostAsync("Token", requestContent);

     if (responseMessage.IsSuccessStatusCode)
     
         TokenResponseModel response = await responseMessage.Content.ReadAsAsync<TokenResponseModel>();
         return response.AccessToken;
     

     return "";

在我的 Mvc 操作中,我称之为:

public async Task<ActionResult> Index()

     var token = await GetBearerToken("http://localhost:6144/", "teste", "123456");

     using (var client = new HttpClient())
     
         client.DefaultRequestHeaders.Add("Authorization", "Bearer "+ token);

         var response = await client.GetAsync("http://localhost:6144/api/values");

         if (response.IsSuccessStatusCode)
         
             var data = response.Content.ReadAsAsync<IEnumerable<string>>();

             return Json(data.Result, JsonRequestBehavior.AllowGet);
         
        

一切正常...但我需要在所有操作中使用 Web Api... 那么如何保留该令牌(尽管每次请求都会获得一个新令牌)以及如何验证它是否已过期......是否可以以某种方式将其与身份验证 cookie 保存在一起? 处理这种情况的任何最佳实践?

谢谢

【问题讨论】:

这里只是一个注释,当您发布到 /token 时,响应包含一个 access_token 可用作 Web API 调用的不记名身份验证,但它也返回一个 set-cookie 标头,该标头会自动在本地保存一个 cookie从 $.ajax 调用,这个 cookie 将授权 MVC 调用。所以对于两个控制器(mvc 和 webapi)来说 1 次登录操作就足够了,我个人将 access_token 保存在 localStorage 中用于 web api 调用; 【参考方案1】:

如果我理解正确,您的 MVC 5 客户端应用程序正在访问其他应用程序的 WebAPI。 MVC 5 客户端使用 cookie 对用户进行身份验证。要访问 WebAPI,您需要从 /Token 端点获取一个 Bearer 令牌并将其发送到 Authorization 标头中。

您无需从客户端 javascript 代码调用 WebAPI,您只需从运行在 MVC5 应用程序服务器上的 MVC 操作中调用它。

在每次服务调用之前获取新令牌听起来是错误的。这意味着每次 2 次往返。这不可能是高性能的。

如果我猜对了,你可以:

    将令牌存储在 Session 对象中。只要您的 MVC 应用程序用户通过身份验证并且他的会话处于活动状态,那么您将始终拥有相同的令牌。 如果它已过期,您将从 WebAPI 获得 401 未经授权的访问。 为了使您的 MVC 操作单元可测试,您可以将会话访问包装到您注入到操作中的服务中(依赖注入)。

    您可以将令牌存储在类似于已经存在的身份验证 cookie 的 cookie 中。这样你就不需要服务器端的 Session 了。再次在这里,我将从 Cookie 获取 Token 的访问权限包装在您所有操作都使用的服务中。

我会使用 Session 存储。简单的。直线前进。 但也许我错过了什么

希望这对您有所帮助。 感谢反馈:-)

【讨论】:

嗨,这与我想使用的方法相同。我只需要你的建议。我计划将 accesstoken 存储在身份验证 cookie 中,并通过在 Application_PostAuthenticateRequest 方法中读取服务器上的 cookie 来设置 HttpContext 的 IPrincipal 对象(用于用户名和角色)。只是需要你的建议。 有什么方法可以让 cookie 和 token 的有效性保持同步以避免 401 错误?在我的应用程序中,我的 cookie 仍然有效,所以我仍然登录,但由于我的令牌即将过期,我从我的 Web API 收到 401 错误。【参考方案2】:

不记名令牌不是授权 Web 应用程序的好方法。如果您将服务的令牌存储在 cookie 中,它将可供应用程序的客户端使用,因此服务层将容易受到应用程序客户端的攻击。唯一的解决方案似乎是在会话中保留令牌,但您将失去应用程序的无状态特性。

Here is describied what/how bearer token should be used: "不记名令牌只是一个大的随机字符串,客户端必须在每次 API 调用时提供。不记名令牌很简单,因为两端不需要特殊的签名或验证码。客户端负责存储将令牌放在安全的地方并随每个请求一起发送。服务器负责在数据库中查找令牌并确保它是有效的——就是这样。”。

这是在single page application 中使用不记名令牌的好例子,客户端直接与服务对话。

无论如何,我建议您使用HMAC authentication、BCrypt 或 ClientCertificates。甚至 amazon 也使用它来验证 REST 请求。

【讨论】:

【参考方案3】:

如果您想管理所有操作中的令牌,您应该更改代码以使用自定义授权过滤器。该过滤器可以添加到所有 Web API 请求、控制器的所有操作或单个操作。为此,从 AuthorizeAttribute 派生并从过滤器发出 GetBearerToken 调用。将令牌粘贴到 HTTP 上下文中,以便在请求处理期间使用。您可以使用工厂生成它们并添加适当的令牌进行身份验证,而不是直接调用创建 HttpClient 实例。

至于确定令牌是否过期,您可以添加一个额外的过滤器来检查返回的特定错误,或者在授权过滤器中发出检查。我不知道您的所有要求,因此很难在那里确定合适的解决方案。

【讨论】:

以上是关于MVC .NET cookie 身份验证系统通过令牌身份验证访问 Web Api的主要内容,如果未能解决你的问题,请参考以下文章

使用 ASP.NET 5 MVC 6 Web API 进行 Cookie 身份验证

我们可以在不使用 ASP.Net Identity 的情况下使用 MVC 5 提供的 cookie 身份验证吗?

ASP.NET Core MVC:设置身份 cookie 过期

同时对 Asp.Net core 2.0 MVC 应用程序 (cookie) 和 REST API (JWT) 进行身份验证

从 ASP.NET Core 1.1 MVC 迁移到 2.0 后,自定义 cookie 身份验证不起作用

ASP.NET MVC - 在匿名 Ajax 请求上刷新 Auth Cookie