1

I'm building a website that will have an MVC side and will get its data from a WebAPI backend of our own, but most likely hosted on a different server (or even on Azure). We're going to use Forms authentication.

Since we want users to only need to log-in once (to the MVC website), what would be the recommended way to transparently authenticate users to the WebAPI backend with the same information that they entered on the MVC forms authentication login?

Since this authentication works based on cookies, would the best way be to call the WebApi authentication Action/Method on the Login action of the MVC app, get the auth cookie for webapi and use it on every call to the WebAPI back end?

Any guidance is greatly appreciated

4

1 回答 1

4

GR7,我不能说我曾经尝试过你正在做的事情。

让我指出一些困扰我的关于你的想法的事情,以及我认为你如何让它发挥作用。

您有一个在一个 Web 服务器上运行的 ASP.NET MVC 应用程序,以及一个在另一台服务器上运行的 ASP.NET WebAPI 应用程序。您想从另一个上使用 cookie。MVC 应用程序中的 cookie 如何对 WebAPI 应用程序有效?即使用户的用户名和密码在两个系统上相同,两个不同应用程序生成的 cookie 也不会相同吧?需要明确的是,我不是 100% 确定,这只是一个怀疑。

这是我怀疑的基础——假设您在 Azure 云上运行一个 ASP.NET MVC 应用程序,并且您对其进行了负载平衡(这意味着您实际上有多个实例,每个实例都在不同的物理机器上运行)。用户连接到您的网站,并在该实例上进行身份验证。然后他导航到该网站上的另一个页面,负载均衡器最终将他发送到另一个实例上的那个页面。我相信在这种情况下他将需要重新进行身份验证,因为他的 cookie 无效,即使它是完全相同的 MVC 应用程序。这种情况的解决方案是在所有机器上设置相同的机器密钥。

这在 MSDN 上讨论:http: //msdn.microsoft.com/en-us/library/eb0zx8fc (v=vs.100).aspx

还有一篇微软知识库文章:http: //support.microsoft.com/kb/910443

还有一些 StackOverflow 文章讨论了这一点: Forms Authentication 是否与 Web 负载均衡器一起工作?Azure 中的 .NET Forms 身份验证 - 多个 VM 需要进行哪些更改?

所以我猜你应该能够在两个 Web 服务器上将机器密钥设置为相同,然后将 cookie 从 MVC 应用程序传递给 WebAPI 应用程序,以免用户进行两次身份验证。如果我错了,希望有人能纠正我。

另一种解决方案是只保留两个 cookie,一个用于 MVC 应用程序,另一个用于 Web API。您需要弄清楚 webapi cookie 的存储位置——因为 ASP.NET 以无状态方式工作,所以每次用户单击不同的 MVC 页面时,这基本上是一个全新的事务。因此,也许您希望用户浏览器存储这两个 cookie。他第一次进行身份验证时,您在 MVC 应用程序和 WebAPI 应用程序上使用相同的用户名和密码对他进行身份验证,然后将两个 cookie 发送回他(当然 MVC cookie 会自动返回给他)。因此,每次他导航到不同的页面时,您都会将两个 cookie 发送到您的 MVC 应用程序,它必须获取其中一个并使用它调用 WebAPI 应用程序。您可能需要确保两个 cookie 的名称不同(默认情况下,两者都是 ASPXAUTH)。您可以使用在 web.config 中更改 MVC cookie 的名称

<authentication mode="Forms">
  <forms name="MyAuthCookie" loginUrl="LoginPage.aspx" />
</authentication>

这应该允许您在用户的浏览器上存储 2 个 cookie,并帮助您区分它们。我假设您的 MVC 和 WebAPI 都在同一个域上,否则浏览器将不会接受 WebAPI cookie(或者至少不会在后续请求中将其传回给您)。

如果这个答案有帮助,请投票,我几乎没有任何代表:)

=======================================

编辑 - 添加此内容以回答您在下面的问题 - 您想知道如何实际获取 WebAPI 提供给您的 MVC 应用程序的 cookie,并将其返回给用户的浏览器。我将从您的 mvc 应用程序将带有您的凭据的 http post 请求发送到您的 webapi 开始,这样您就可以清楚地了解所有内容。

让我们使用 JSON 将登录信息作为 HTTP 请求的一部分从您的 MVC 应用程序发送到您的 Web API 服务器。在 MVC 应用程序的 Models 文件夹中创建一个模型,如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SitterWebsite.Models
{
    public class MyJsonLoginModel
    {
        public string UserName;
        public string Password;
        public bool RememberMe;
    }
}

然后在 ActionController.cs 中的 Login() 方法中,您可以添加类似这样的内容来发出请求

string loginapibaseaddress = "http://mywebapiurl.com/";
string loginapiaddress = "api/AccountAPI/SignMeIn";

MyJsonLoginModel mydatamodel = new MyJsonLoginModel()
{
        UserName = "gary",
        Password = "password",
        RememberMe = false,
};

// Create the JSON formatter.
MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();

// Use the JSON formatter to create the content of the request body.
HttpContent content = new ObjectContent<MyJsonLoginModel>(mydatamodel, jsonFormatter);

// I am going to return the cookie received from the Web API controller to the browser
// I will obtain it from the HTTP POST request to the WebAPI in the form of a Cookie object
// I will then need to convert it to an HttpCookie object
Cookie cookietosendback = new Cookie();   // cookie of type System.Net.Cookie
HttpCookie httpcookietosendback = new HttpCookie("will_name_later");   // cookie of type System.Web.HttpCookie


// Create a new cookie container. 
// We will attach this cookie container to the HTTP request
// The Web API auth cookie will automatically be put into this container
CookieContainer cookie_container = new CookieContainer();

HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookie_container;
HttpClient loginclient = new HttpClient(handler);

// Set the base address of the client
loginclient.BaseAddress = new Uri(loginapibaseaddress);

// Set the web api address of the client
Uri loginapiaddressuri = new Uri(loginapibaseaddress+loginapiaddress);

// Send an HTTP POST request
HttpResponseMessage response = loginclient.PostAsync(loginapiaddressuri, content).Result;


Cookie mycookie;

if (response.IsSuccessStatusCode) 
{

    // Now let's access the cookies from the cookie container since it will be automatically populated with any
    // cookies returned by our http request (ie. any cookies in the http response). 

    IEnumerable<Cookie> responseCookies = cookie_container.GetCookies(loginapiaddressuri).Cast<Cookie>();

    foreach (Cookie cookie in responseCookies)
    {
    if cookie.Name.Equals('.ASPXAUTH')
        cookietosendback = cookie
    }

    // We want to return the cookie to the users browser
    // However the HttpContext.Response.Cookies.Add() method needs an HttpCookie object, not a Cookie object
    // So we need to convert the Cookie to an HttpCookie
    httpcookietosendback.Name = "GaryCookie"; // changing name since both MVC and WebAPI name their cookie .ASPXAUTH
    httpcookietosendback.Value = cookietosendback.Value;
    httpcookietosendback.Path = cookietosendback.Path;
    httpcookietosendback.Expires = cookietosendback.Expires;
    httpcookietosendback.Domain = cookietosendback.Domain;  
    // Note - if the domain of your WebAPI is different from the MVC app, you might want to change it in
    // above statement, otherwise the browser will either not accept a cookie from another domain, or it will
    // definitely not pass it to the mvc app in your next request

    this.ControllerContext.HttpContext.Response.Cookies.Add(httpcookietosendback);

}
else
{
    // Http post to webapi failed
}

因此,现在当您进入登录页面并输入您的凭据并点击提交时,您不仅会获得通常获得的 MVC cookie,还会获得 WebAPI cookie。根据我上面的代码,它将被命名为“GaryCookie”。每次您转到网站上的另一个页面时,您的浏览器都会请求该页面并将两个 cookie 发送到您的 mvc 应用程序。如果您希望调用其他 WebAPI 方法,您现在需要做与我刚才所做的相反的操作,即获取修改后的 WebAPI cookie “GaryCoookie”并将其重命名为原来的名称。然后在对 WebAPI 方法发出 GET 或 POST 请求时将其与标头一起发送。

如果您的 webapi 和 mvc 应用程序不在同一个域中,您还应该设置 cookie 的域以匹配 MVC 的域。否则,如果您请求另一个页面,您的浏览器不会将 cookie 发送到 MVC 应用程序。

顺便说一句,我刚刚测试了所有这些,所以它有效。

于 2013-05-05T01:22:10.527 回答