【问题标题】:MVC to WebAPI authentication for the same application同一应用程序的 MVC 到 WebAPI 身份验证
【发布时间】:2013-05-04 17:22:04
【问题描述】:

我正在构建一个具有 MVC 端的网站,并将从我们自己的 WebAPI 后端获取其数据,但很可能托管在不同的服务器上(甚至在 Azure 上)。我们将使用 Forms 身份验证。

既然我们希望用户只需要登录一次(登录到 MVC 网站),那么推荐的方法是使用他们在 MVC 表单身份验证登录时输入的相同信息透明地向 WebAPI 后端验证用户身份?

由于此身份验证基于 cookie 工作,最好的方法是在 MVC 应用程序的登录操作上调用 WebApi 身份验证操作/方法,获取 webapi 的身份验证 cookie 并在每次调用 WebAPI 后端时使用它?

非常感谢任何指导

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-4 asp.net-web-api forms-authentication


    【解决方案1】:

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

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

    您有一个 ASP.NET MVC 应用程序在一个 Web 服务器上运行,一个 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 文章讨论这个: Does Forms Authentication work with Web Load Balancers?.NET Forms Authentication in Azure - What changes are required for multiple VMs?

    所以我猜你应该能够在两个 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 应用程序向您的 webapi 发送带有您的凭据的 http post 请求开始,这样您就可以清楚地了解所有内容。

    让我们使用 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 应用程序。

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

    【讨论】:

    • 嗨,加里,感谢您的回复。我没有说我想对两个系统使用相同的 cookie,我说我的想法是,当用户登录到 MVC 网站时,在 LogOn 操作中,我可以访问纯文本的用户名和密码,在哪一点我认为我可以使用相同的凭据进行 webapi 调用以获取 webapi auth cookie,然后将该 cookie 用于任何进一步的 webapi 调用。你做过吗?
    • GR7,感谢您的澄清。你说的是对的。这基本上就是我在回复的第二部分中所说的(以“另一种解决方案......”开头)。当您的用户登录 MVC 网站时,您将能够访问用户名和密码。假设您使用为您生成的默认 C# 代码使用表单身份验证。如果您查看 AccountController.cs,您会看到登录操作。 public ActionResult Login(LoginModel model, string returnUrl){ }
    • 在该方法中,您可以使用 model.UserName 和 model.Password 访问用户名和密码。然后,您应该能够调用您的 WebAPI 并从中获取身份验证 cookie。
    • 但是请注意,此身份验证 cookie 已由 WebAPI 发送到您的 MVC 应用程序,它尚未发送到您用户的浏览器。您需要确保您还在这里编写了一些代码以将 cookie 返回到浏览器!否则,不是登录到 webAPI 的用户,只有您的 MVC 应用程序具有 webapi 的身份验证 cookie。一旦交易完成,它将删除该 cookie,因此在未来的请求中,您将不会登录到 webAPI。这就是为什么我之前谈到将 webapi auth cookie 与 mvc auth cookie 一起发送给用户的原因。
    • 当然我打算添加获得的 webapi cookie 以用于任何进一步的请求,但是,我还不清楚。我如何将获得的 cookie 添加给用户,以便将其与任何进一步的 ajax webapi 请求一起发送?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2015-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多