【问题标题】:Accessing User data in WebAPI from Context/Cookie从 Context/Cookie 访问 WebAPI 中的用户数据
【发布时间】:2013-04-23 09:18:56
【问题描述】:

更新:

我已将代码更改为FormsAuthentication.SetAuthCookie(_model.UserName, true);。我确实有 2 个 Web.config 文件,1 个用于 MVC,另一个用于 WebAPI。在 MVC 配置中,我定义了

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

两个应用程序都在同一个域中。


更新:我们是否应该在 WebAPI 中使用 cookie?


目前,我有一个使用表单身份验证的 MVC 项目和一个 WebAPI 项目。问题是我无法在 WebAPI 项目中获取与请求关联的用户。我认为这是可能的,或者可能实现是错误的?

注意:我将 cookie 代码放在 WebAPI 控制器方法中作为测试,它不是应该在的地方。

MVC - 处理登录请求,创建身份验证票。

// POST: /Account/Login
[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel _model, string _returnUrl)
{
    if (ModelState.IsValid)
    {
        if (Membership.ValidateUser(_model.UserName, _model.Password))
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(_model.UserName, true, 15);
            string encryptedTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
            Response.Cookies.Add(cookie);

            // set redirect
        }
    }

    // If we got this far, something failed, redisplay form
    return View(_model);
}

WebAPI - 处理更新请求

[AcceptVerbs("PUT")]
public HttpResponseMessage UpdateInfo(int _id, ExampleModel _model)
{
    try
    {
        HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];

        if (authCookie != null)
        {
            string encTicket = authCookie.Value;

            if (!String.IsNullOrEmpty(encTicket))
            {
                // decrypt the ticket if possible.
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encTicket);

                var userData = ticket.UserData;
            }
        }

        // do update
        return Request.CreateResponse(HttpStatusCode.OK, data, GlobalConfiguration.Configuration);
    }
    catch (Exception err)
    {
        m_logger.Error(err);
        throw;
    }
}

【问题讨论】:

  • 所以cookie是空的?
  • 是的,cookie 为空。
  • 这两个网站是否在同一个域中?
  • 两个站点在同一个域中。
  • 如果你使用fiddler,你真的能看到cookie被发送到web api请求吗?

标签: asp.net-mvc asp.net-web-api


【解决方案1】:

为什么要手动创建表单身份验证票?你为什么不能这样做?

if (Membership.ValidateUser(_model.UserName, _model.Password))
{
    FormsAuthentication.SetAuthCookie(_model.UserName, true);

    // set redirect
}

要控制超时,您可以在 web.config 中进行设置:

<authentication mode="Forms">
    <forms timeout="15" />
</authentication>

如果您这样做,则不必检查 cookie 来获取用户名。它应该自动设置在控制器的 User.Identity.Name 属性上。这适用于 MVC Controllers 和 ApiControllers。

另外,如果您的 MVC 应用程序和 WebAPI 应用程序托管在 Web 服务器的不同节点(或不同服务器)上,则 MVC 项目和 WebAPI 项目的 web.config 中必须有相同的 machineKey文件。它们也必须在同一个域中,但如果它们是具有不同 web.config 文件的独立应用程序,则它们必须具有相同的 machineKey 值(decryptiondecryptionKeyvalidationvalidationKey)。这些是验证、加密和解密 .ASPXAUTH cookie 所需的值。

我们是否应该在 WebAPI 中使用 cookie?

如果您使用上述模式,则不必手动获取任何 cookie,至少用于身份验证/授权。 Cookie 与方法、标头、响应代码等一起是 HTTP 规范的一部分。WebAPI 将它们放在其中是有原因的。如果您需要它们,请使用它们。如果您不需要它们,请不要使用它们。

你应该尽量避免这样做:

HttpCookie authCookie = HttpContext.Current.Request.Cookies["name"];

您可以像这样从 ApiController 获取 cookie:

IEnumerable<CookieHeaderValue> cookies = this.Request.Headers.GetCookies("name");
if (cookies.Any())
{
    IEnumerable<CookieState> cookie = cookies.First().Cookie;
    if (cookie.Any())
    {
        string value = cookie.First().Value;
    }
}

更新

我一直用这个生成工具:http://aspnetresources.com/tools/machineKey

只需单击“生成密钥”按钮,然后将生成的 &lt;machineKey ../&gt; 部分粘贴到两个 web.config 文件的 &lt;system.web&gt; 部分下的某处。

更新

可能存在安全问题,具体取决于谁有权查看您的 web.config 文件。如果您像这样将其签入您的源代码控制存储库,任何可以访问您的源代码的人都可以看到验证和加密密钥。

我所做的有点令人费解,但它确保了这些字符串的安全。您可以改为将validationKeyencryptionKey 值存储在appSettings 中,然后加密配置文件的appSettings 部分(使用CloudConfigCrypto 之类的东西)。这意味着无法访问您的加密证书私钥的任何人都无法读取这些值。然后,您可以使用Microsoft.Web.Administration.ServerManager 类在运行时更改machineKey 值,在Application_Start 期间。不过,有关此问题的更多详细信息超出了此问题的范围。

另一种选择是将它们排除在源代码控制之外,但是您必须在每次部署时手动更新 web.config。

【讨论】:

  • 我更新了代码以反映您的建议(参见 OP),但 User.Identity.Name 仍然为空。你说两个 web 配置应该有相同的 machinekey 值,但是两个配置文件都不包含这个元素。我该如何生成这个?
  • 我更新了我的答案。那里有一些工具,只需谷歌“生成 machineKey”。生成的&lt;machineKey&gt; 元素属于&lt;system.web&gt;,因此是&lt;authentication&gt; 元素的兄弟。
  • 将机器密钥添加到两个 Web 配置工作中,我现在可以访问 User.Identity 并填充预期的数据。谢谢!虽然,将验证密钥存储为这样的长字符串有多大的顾虑?有什么安全问题吗?
  • 我更新了我的答案。安全性可能是一个问题,具体取决于您希望谁访问您的加密和验证密钥。您可以将它们加密为 appSettings 值,然后在运行时将它们读出并以编程方式更新 web.config。
【解决方案2】:

我找到了将表单身份验证与 Web API 和 MVC 应用程序一起使用的最简单方法,方法是使用 WebMatrix.WebData 命名空间中的 WebSecurity 类。它提供了对最常用操作的非常简单的访问,例如创建用户帐户、登录和注销用户、重置或更改密码。而且您不必自己处理会话 cookie。

我在与您类似的设置中使用它,我们必须为 Web API 和 MVC 应用程序使用表单身份验证。

API 示例

登录:

if(WebSecurity.Login(username, password))
{
    return Request.CreateErrorResponse(
        HttpStatusCode.Forbidden, "Invalid credentials");
}
else
{
    return Request.CreateResponse(HttpStatusCode.OK);
}

注册:

if(WebSecurity.UserExists(username))
{
    return Request.CreateErrorResponse(
        HttpStatusCode.BadRequest, "Username already exists");
}

WebSecurity.CreateUserAndAccount(username, password, data);

HttpResponseMessage response = Request.CreateResponse(
    HttpStatusCode.Created, yourUserObject);
    response.Headers.Location = new Uri(
        Url.Link("DefaultApi",
            new { controller = "users", id = WebSecurity.GetUserId(username) }));
return response;

同样的调用适用于 MVC 应用程序。

【讨论】:

    猜你喜欢
    • 2020-08-10
    • 2014-05-10
    • 2013-05-21
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 2019-12-11
    • 2020-11-15
    相关资源
    最近更新 更多