来自AJAX的表单身份验证和POST请求

时间:2009-12-10 06:58:54

标签: asp.net forms-authentication

我们有一个受表单身份验证保护的ASP.NET应用程序。该应用程序大量使用MS AJAX来调用其Web服务。

当表单身份验证超时,并且 GET - 请求发生时 - 一切正常(用户被重定向到登录页面)。

但是当表单身份验证超时且 POST - 请求发生时(ajax) - 没有重定向发生,而应用程序返回“401 unathorized”并且浏览器提示用户名和密码(不是登录表单,而是浏览器内置对话框)。当然输入任何用户名/密码永远不会有帮助。

我该如何处理?

更新:在查看firebug后,我发现常规POST请求重定向到登录正常,只有网络服务调用才会抛出“401 Unauthorizes”。 常规请求和Web服务之间的区别是URL。对于常规的后请求,这是“page.aspx”,对于webservices,这是“service.asmx / MethodName”......

2 个答案:

答案 0 :(得分:2)

好的,回答我自己的任务。

在研究了这个问题并进行了更多研究之后,我发现当一个网络应用程序受到表单身份验证的保护并且用户未经过身份验证时,就会发生这种情况:

  • 如果是GET请求 - 用户是 重定向到登录页面。
  • 如果是对页面的POST请求 - 用户是 重定向到登录页面。
  • 如果是对网络服务的POST请求 - 用户获得401未经授权

这就是ASP.NET的工作原理

如果通过AJAX(xmlHttpRequest对象)调用Web服务并返回401 - 当然浏览器会显示一个弹出式登录框。

现在,您应该怎么做才能向Application_PostAuthenticateRequest添加一些代码,以防止为webservices抛出401.

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
    if (Request.RequestType == "POST" //if its POST
        && !User.Identity.IsAuthenticated //if user NOT authed
        && !HasAnonymousAccess(Context) //if it's not the login page
        )
    {
        //lets get the auth type
        Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
        SystemWebSectionGroup grp = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
        AuthenticationSection auth = grp.Authentication;
        //if it FORMS auth
        if(auth.Mode== AuthenticationMode.Forms)
        {

            //then redirect... this redirect won't work for AJAX cause xmlHttpRequest can't handle redirects, but anyway...
            Response.Redirect(FormsAuthentication.LoginUrl, true);
            Response.End();

        }
    }
}
public static bool HasAnonymousAccess(HttpContext context)
{
    return UrlAuthorizationModule.CheckUrlAccessForPrincipal(
        context.Request.Path,
        new GenericPrincipal(new GenericIdentity(string.Empty), null),
        context.Request.HttpMethod);
}

答案 1 :(得分:1)

我看到两个解决方案:

(1)“心跳”机制。在每个页面上都包含一个脚本,它将通过一些虚拟的ajax请求“ping”服务器,如:

<script>
   setInterval(ping, 60000); // based on comment by John
   function ping()
   {
       $.get('/do/nothing');
   }
</script>

这样,只要浏览器窗口打开,会话就不会过期。

(2)在每个ajax请求上检查响应的状态。如果响应具有“401未授权”代码(或任何其他不同于200的代码),则表示会话已过期,而不是将响应加载到页面中的某个对话框中,而是将用户重定向到登录页面。

基于评论的结论:

最好的解决方案是结合上述两种机制。只要页面显示在浏览器中,心跳机制将有助于使会话保持活动状态。但是并不能保证这一点。会话过期时,可以断开与服务器的连接并重新打开。所以你应该检查响应状态。