【问题标题】:.net Forms Authentication - manually setting HttpContext.Current.User not working in custom AuthorizeAttribute.net Forms Authentication - 手动设置 HttpContext.Current.User 在自定义 AuthorizeAttribute 中不起作用
【发布时间】:2015-12-02 03:31:05
【问题描述】:

我已经打这个几个小时了,我很难过。我正在向 MVC 5 控制器发出 ajax 发布请求,以尝试自动登录特定的预定义“超级”用户。在控制器方法中,我尝试以编程方式设置HttpContext.Current.User并进行身份验证,因此超级用户可以跳过手动登录的过程。对此的共识似乎在这里,我实现了:

setting HttpContext.Current.User

这似乎有效,直到我尝试使用自定义 AuthorizeAttribute 查看任何其他控制器方法。

控制器方法:

[HttpPost]
[AllowAnonymous]
public ActionResult Login(string username)
{
    string password = ConfigurationManager.AppSettings["Pass"];

    User user = service.Login(username, password);

    var name = FormsAuthentication.FormsCookieName;
    var cookie = Response.Cookies[name]; 
    if (cookie != null)
    {   
        var ticket = FormsAuthentication.Decrypt(cookie.Value);
        if (ticket != null && !ticket.Expired)
        {
            string[] roles = (ticket.UserData as string ?? "").Split(',');
            System.Web.HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
        }
    }

    //...processing result

    return Json(result);
}

上面的 service.Login 方法创建了 cookie:

FormsAuthentication.SetAuthCookie(cookieValue, false);

虽然我设置了具有 Identity 且 IsAuthenticated 为 true 的用户,但下面的 filterContext.HttpContext.User 不是同一个用户。它本质上是空的,就好像它从未被分配过,也没有经过身份验证。

public override void OnAuthorization(AuthorizationContext filterContext) 
{
    string[] userDetails = filterContext.HttpContext.User.Identity.Name.Split(char.Parse("|"));
}

我能找到的最接近的帖子在这里:IsAuthenticated works on browser - but not with Air client!

但是,我已经解决了这个问题:

<authentication mode="Forms">
  <forms cookieless="UseCookies" timeout="60" loginUrl="~/Account/Login" />
</authentication>

为了使 AuthorizationContext.User 与我在控制器中进行身份验证的 HttpContext.Current.User 匹配,我缺少什么?

更新:

我意识到我需要重定向才能正确设置 cookie,但我无法通过 ajax 调用远程使其工作。这是在站点 B 上执行控制器方法时站点 A 中的脚本的样子。此重定向不会设置会话。在下一个控制器方法上对用户进行身份验证时,它仍然不存在。它只是将我重定向回登录视图。

function remoteLogin(id) {
    $.ajax({
        url: "/MyController/RemoteLogin",
        type: "POST",
        dataType: "json",
        data: { "id": id }
    }).done(function (data) {
        if (data) {
            if (data.user) {
                var user = data.user;
                $.ajax({
                    url: "http://siteB.xyz/Account/Login",
                    type: "POST",
                    dataType: "json",
                    data: { "username": user.username, "password": user.password }
                }).done(function (data) {
                    if (data) {
                        window.location.href = "http://siteB.xyz/Next"
                    } else {
                        alert("Fail.");
                    }
                }).fail(function (data) {
                    alert("Fail.");
                });
            } else {
                alert("Fail.");
            }
        }
    }).fail(function (data) {
        alert("Fail.");
    });
}

【问题讨论】:

    标签: .net asp.net-mvc forms-authentication httpcontext


    【解决方案1】:

    您遇到的问题是此时您只是设置身份验证 cookie,在表单身份验证模块中创建的 IPrincipal 在有新请求之前不会发生 - 所以此时 HttpContext.User 在一个奇怪的状态。一旦发生重定向,因为它是来自浏览器的新请求,cookie 将在到达您的页面并创建正确的用户对象之前被读取。

    Cookie 仅在请求完成后在浏览器上设置。

    【讨论】:

    • 这就是我自己发现的,环顾四周-我正在寻找的是一种在我正在尝试的上下文中执行此操作的方法-远程登录。例如,在我的脚本中将“location.href”设置为成功并没有任何区别。我需要一种方法来通过 ajax 调用远程执行您所描述的操作。
    • 确认!我所做的(而且它的安全性值得怀疑)是在 App A 端使用用户名/密码编写一个 cookie,然后在 ajax 调用成功后从脚本打开一个新窗口。在 App B 中,我读取并销毁临时 cookie,执行登录,然后重定向到经过身份验证的视图。这一切似乎都奏效了。稍后会用详细信息更新我的 OP。
    【解决方案2】:

    问题与您的操作代码相对于请求处理管道的运行位置有关。您的代码在 ProcessRequest 步骤中运行(见下文)。

    HttpContext.Current.User 应该由 AuthenticateRequest 事件处理程序设置。 FormsAuthenticationModule 会处理这个问题,将 Request.CookiesFormsAuthenticationCookie 重新转换为 FormsAuthenticationTicket,然后转换为 IPrincipal。该 Principal 被设置为 Request.CurrentUser 我相信 Thread.CurrentPrincipal

    这需要在 AuthenticateRequest 步骤中完成,因为请求缓存可能因用户而异 (ResolveRequestCache) 并且会话状态总是如此 (AcquireRequestState >)。此外,AuthorizeRequest 步骤决定在 AuthenticateRequest 步骤中设置的用户主体是否具有通过 AuthorizeRequest 步骤所需的权限,而无需获得401 或 300 级重定向到登录页面(我相信 MVC 和 WebAPI 的授权机制是 ProcessRequest 的一部分)

    • 开始请求
    • AuthenticateRequest (FormsAuthenticationModule)
    • AuthorizeRequest(例如 UrlAuthorizationModule)
    • 解决请求缓存
    • MapRequestHandler
    • 获取请求状态
    • PreRequestHandlerExecute
    • ProcessRequest(HttpHandler、Web 窗体、MVC 控制器操作在此处显示)

    您可以尝试通过将 HttpContext.Current.User Thread.CurrentPrincipal 设置为您的 IPrincipal 来强制执行此操作>,但它只适用于在 ProcessRequest 阶段之后运行的代码......关于会话状态、缓存和授权的重要决定已经做出。

    理论上,您可以通过编写一个 HttpModule 并在 AuthenticateRequest(或 global.asax)中/之前实现它来实现类似于您尝试做的事情,但您还不会可以访问更高级别的 MVC 控制器概念、会话状态等。您可以检查 HttpContext.Request.QueryStringHttpContext.Request.Form >HttpContext.Request.Cookies,但仅此而已。您必须从 HTTP 请求中的 AJAX 调用中获取用户名,然后调用 FormsAuthentication.SetAuthCookie() 或创建 FormsAuthenticationTicketFormsAuthenticationCookie 自己并将 cookie 填充到 Request.CookiesResponse.Cookies 中。当您的控制器逻辑运行时,我很确定该请求将显示为已通过身份验证。

    【讨论】:

    • @scott732 - 非常感谢您的深入回应。听起来比我最终采用的方法更复杂。不过,这一切都很好,谢谢。
    猜你喜欢
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2016-06-15
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多