我使用$.post()使用Ajax调用servlet,然后使用生成的HTML片段替换用户当前页面中的div元素。但是,如果会话超时,服务器将发送重定向指令,将用户发送到登录页面。在本例中,jQuery用登录页面的内容替换div元素,迫使用户的眼睛看到一个罕见的场景。

如何使用jQuery1.2.6管理Ajax调用的重定向指令?


当前回答

作为ajax的替代,正在开发一个新的Fetch API,它允许手动重定向处理。您需要检查当前的浏览器支持是否足以满足您的需要。

其他回答

我只想锁定整个页面的任何ajax请求@SuperG让我开始了。以下是我的结论:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

我想特别检查某些http状态代码以作为我的决定的基础。然而,您可以绑定到ajaxError以获得除成功之外的任何结果(可能只有200?)

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

我用@John和@Arpad链接以及@RobWinch链接的答案得到了一份工作解决方案

我使用Spring Security 3.2.9和jQuery 1.10.2。

扩展Spring的类以仅从AJAX请求引起4XX响应:

public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
        super(loginFormUrl);
    }

    // For AJAX requests for user that isn't logged in, need to return 403 status.
    // For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
    @Override
    public void commence(final HttpServletRequest request,
                         final HttpServletResponse response,
                         final AuthenticationException authException)
            throws IOException, ServletException {
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
        } else {
            super.commence(request, response, authException);
        }
    }
}

applicationContext-security.xml

  <security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
    <security:form-login login-page='/login.jsp' default-target-url='/index.jsp'                             
                         authentication-failure-url="/login.jsp?error=true"
                         />    
    <security:access-denied-handler error-page="/errorPage.jsp"/> 
    <security:logout logout-success-url="/login.jsp?logout" />
...
    <bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
        <constructor-arg value="/login.jsp" />
    </bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
    <property name="requestMatcher">
      <bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
        <constructor-arg>
          <bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
            <constructor-arg>
              <bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
            </constructor-arg>
            <constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
            <property name="useEquals" value="true"/>
          </bean>
        </constructor-arg>
      </bean>
    </property>
</bean>

在我的JSP中,添加一个全局AJAX错误处理程序,如下所示

  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
      if ( jqxhr.status === 403 ) {
          window.location = "login.jsp";
      } else {
          if(thrownError != null) {
              alert(thrownError);
          } else {
              alert("error");
          }
      }
  });

此外,从JSP页面中的AJAX调用中删除现有的错误处理程序:

        var str = $("#viewForm").serialize();
        $.ajax({
            url: "get_mongoDB_doc_versions.do",
            type: "post",
            data: str,
            cache: false,
            async: false,
            dataType: "json",
            success: function(data) { ... },
//            error: function (jqXHR, textStatus, errorStr) {
//                 if(textStatus != null)
//                     alert(textStatus);
//                 else if(errorStr != null)
//                     alert(errorStr);
//                 else
//                     alert("error");
//            }
        });

我希望它能帮助其他人。

更新1我发现我需要将选项(始终使用默认target=“true”)添加到表单登录配置中。这是需要的,因为在AJAX请求被重定向到登录页面后(由于会话过期),Spring会记住之前的AJAX请求,并在登录后自动重定向到它。这将导致返回的JSON显示在浏览器页面上。当然,不是我想要的。

更新2不要总是使用默认target=“true”,而是使用@RobWinch示例来阻止来自requstCache的AJAX请求。这允许正常链接在登录后重定向到其原始目标,但AJAX在登录后会转到主页。

最终实现的解决方案是为Ajax调用的回调函数使用包装器,并在该包装器中检查返回的HTML块上是否存在特定元素。如果找到元素,则包装器执行重定向。如果没有,包装器将调用转发给实际的回调函数。

例如,我们的包装器函数类似于:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

然后,在进行Ajax调用时,我们使用了类似的方法:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

这对我们很有用,因为所有Ajax调用都会在DIV元素中返回HTML,我们使用该元素替换页面的一部分。此外,我们只需要重定向到登录页面。

我只是想分享我的方法,因为这可能会帮助某人:

我基本上包含了一个JavaScript模块,它处理身份验证,例如显示用户名,以及本例中处理重定向到登录页面。

我的场景:我们之间基本上有一个ISA服务器,它监听所有请求,并用302和位置标头响应登录页面。

在我的JavaScript模块中,我最初的方法是

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

问题(这里已经提到了很多)是浏览器自己处理重定向,因此我的ajaxComplete回调从未被调用,但我得到了已经重定向的Login页面的响应,这显然是状态200。问题是:如何检测成功的200响应是您实际的登录页面还是其他任意页面??

解决方案

由于无法捕获302个重定向响应,我在登录页面上添加了LoginPage标头,其中包含登录页面本身的url。在模块中,我现在侦听标头并执行重定向:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

…这就像魅力一样:)。你可能想知道为什么我在LoginPage头中包含url。。。基本上是因为我找不到从xhr对象自动定位重定向得到的GET url的方法。。。

这对我有用:

success: function(data, textStatus, xhr) {

        console.log(xhr.status);
}

一旦成功,ajax将获得浏览器从服务器获得的相同状态代码并执行它。