.net表单身份验证 - 手动设置HttpContext.Current.User在自定义AuthorizeAttribute中不起作用

时间:2015-12-02 03:31:05

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

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

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的User,但下面的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.");
    });
}

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

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

HttpContext.Current.User 应该由 AuthenticateRequest 事件处理程序设置。 FormsAuthenticationModule 负责处理此事,转而使用 Request.Cookies &#39; FormsAuthenticationCookie 返回 FormsAuthenticationTicket ,然后转换为 IPrincipal 。该Principal设置为 Request.CurrentUser ,我相信 Thread.CurrentPrincipal

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

  • 的BeginRequest
  • AuthenticateRequest(FormsAuthenticationModule)
  • AuthorizeRequest(例如,UrlAuthorizationModule)
  • ResolveRequestCache
  • MapRequestHandler
  • 的AcquireRequestState
  • 的PreRequestHandlerExecute
  • ProcessRequest(HttpHandler,Web窗体,MVC控制器操作在这里生活)

您可以尝试通过将 HttpContext.Current.User Thread.CurrentPrincipal 设置为 IPrincipal ,但它仅适用于在 ProcessRequest 阶段中该点之后运行的代码...已经进行了有关会话状态,缓存和授权的重要决策。

理论上,您可以通过编写HttpModule并在 AuthenticateRequest (或global.asax)之前/之前实现它来实现类似于您尝试做的事情,但是您不会&# 39;但是还可以访问更高级别的MVC控制器概念,会话状态等。您可以检查 HttpContext.Request.QueryString HttpContext.Request.Form ,和 HttpContext.Request.Cookies ,但其他并不多。您必须从HTTP请求中获取AJAX调用中的用户名,并调用 FormsAuthentication.SetAuthCookie()或创建 FormsAuthenticationTicket FormsAuthenticationCookie 您自己并将Cookie填入 Request.Cookies Response.Cookies 。当您的控制器逻辑运行时,我非常确定该请求将显示为已验证。