page - window redirect using javascript




jQuery Ajax 호출 후 리디렉션 요청을 관리하는 방법 (20)

$.post() 를 사용하여 Ajax를 사용하여 서블릿을 호출 한 다음 결과 HTML 부분을 사용하여 사용자의 현재 페이지에서 div 요소를 대체합니다. 그러나 세션 시간이 초과되면 서버는 redirect 지시문을 보내 사용자를 로그인 페이지로 보냅니다. 이 경우 jQuery는 div 요소를 로그인 페이지의 내용으로 대체하여 사용자 눈에 희귀 한 장면을 실제로 보게합니다.

jQuery 1.2.6을 사용하여 Ajax 호출에서 리디렉션 지시문을 관리하려면 어떻게해야합니까?


301 및 302 응답을 올바르게 처리하는 브라우저는 없습니다. 그리고 실제로이 표준은 심지어 Ajax 라이브러리 벤더들에게는 그들을 "투명하게"다루어야한다고 말했습니다. Ra-Ajax 에서는 서버에서 투명하게 리디렉션을 처리하기 위해 HTTP 응답 상태 코드 278 (일부 "사용되지 않은"성공 코드)을 사용해야했습니다.

이것은 정말로 저를 귀찮게합니다. 만약 누군가 W3C에 "끌어 들이기"를한다면 W3C 301과 302 코드를 처리 할 필요가 있음을 W3C에 알릴 수 있다는 점에 감사 할 것입니다 ...! ;)


@Steg의 설명에 따라 문제를 다시 인용하겠습니다.

나는 너와 비슷한 문제가 있었다. 2 가지 가능한 응답을 가진 ajax 요청을 수행합니다. 브라우저에서 새 페이지로 리디렉션하는 요청과 현재 페이지의 기존 HTML 양식을 새 페이지로 대체하는 요청입니다.

IMHO 이것은 진정한 도전이며 공식적으로 현재 HTTP 표준으로 확장되어야합니다.

나는 새로운 Http Standard가 새로운 상태 코드를 사용할 것이라고 믿는다. 의미 : 현재 301/302 는 브라우저 에게이 요청의 내용을 새로운 location 가져 301/302 지시합니다.

확장 표준에서는 응답 status: 308 경우 브라우저가 기본 페이지를 제공된 location 리디렉션해야한다고 말합니다.

그것은 말하고있다. 나는이 미래의 행동을 이미 모방하려는 경향이 있으므로 document.redirect가 필요할 때 나는 서버를 다음과 같이 응답한다.

status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html

JS가 " status: 204 "를 얻으면 x-status: 308 헤더의 존재 여부를 검사하고 location 헤더에 제공된 페이지로 document.redirect를 수행합니다.

이게 너에게 의미가 있니?


Vladimir Prudnikov와 Thomas Hansen이 한 말 :

  • 서버 측 코드를 변경하여 XHR인지 확인하십시오. 그렇다면 리디렉션의 응답 코드를 278로 설정하십시오. django에서 :
   if request.is_ajax():
      response.status_code = 278

이렇게하면 브라우저가 응답을 성공으로 처리하여 Javascript로 넘깁니다.

  • JS에서 폼 제출이 Ajax를 통해 이루어 졌는지 확인하고, 응답 코드를 확인하고 필요한 경우 리디렉션하십시오.
$('#my-form').submit(function(event){ 

  event.preventDefault();   
  var options = {
    url: $(this).attr('action'),
    type: 'POST',
    complete: function(response, textStatus) {    
      if (response.status == 278) { 
        window.location = response.getResponseHeader('Location')
      }
      else { ... your code here ... } 
    },
    data: $(this).serialize(),   
  };   
  $.ajax(options); 
});

값을 전달하려는 경우 세션 변수를 설정하고 예 : 액세스 할 수 있습니다. JSP에서는 다음과 같이 작성할 수 있습니다.

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

그리고 나서 자바 스크립트 변수에이 임시 값을 저장하고 놀 수 있습니다.


나는 Timmerz의 방법이 레몬의 약간의 비틀기를 좋아한다. JSON을 기대할 때 text / html의 contentType 이 반환되면 대부분 리디렉션됩니다. 제 경우에는 페이지를 다시로드하기 만하면 로그인 페이지로 리디렉션됩니다. 아, jqXHR 상태가 200인지 확인하십시오. 오류 기능을 사용하고 있기 때문에 어리석은 것 같습니다. 그렇지 않으면 합법적 인 오류가 반복적으로 다시로드됩니다 (oops)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});

나는 login.php 페이지에 다음을 넣어 이것을 해결했다.

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>

나는 누군가에게 도움이 될지도 모르기 때문에 나의 접근 방식을 공유하기를 원했습니다.

기본적으로 사용자 이름 표시 및이 경우 로그인 페이지로 리디렉션을 처리하는 것과 같은 인증 작업을 처리하는 JavaScript 모듈이 기본적으로 포함되어 있습니다.

내 시나리오 : 우리는 기본적으로 모든 요청을 듣고 302 페이지와 위치 헤더 를 사용하여 로그인 페이지에 응답 하는 ISA 서버를 가지고 있습니다.

내 JavaScript 모듈에서 내 초기 접근 방식 은 다음과 같습니다.

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

문제 (이미 언급 한 많은 것)는 브라우저가 리다이렉션을 처리하기 때문에 내 ajaxComplete 콜백이 호출 된 적이 없기 때문에 status 200 이미 리디렉션 된 로그인 페이지응답을 얻었습니다. 문제 : 성공적인 200 응답이 실제 로그인 페이지인지 또는 다른 임의 페이지인지 여부를 어떻게 감지합니까?

해결책

302 리디렉션 응답을 캡처 할 수 없었기 때문에 로그인 페이지 자체에 URL이 포함 된 로그인 페이지 헤더에 LoginPage 헤더를 추가했습니다. 이제 모듈에서 헤더를 수신하고 리디렉션을 수행합니다.

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

... 그리고 그건 매력처럼 작동합니다 :). 왜 LoginPage 헤더에 URL을 포함시켜야하는지 궁금해 할 것입니다 ... xhr 객체에서 자동 위치 재 지정으로 인해 GET URL을 결정할 방법이 없기 때문에 기본적으로 ...


나는이 문제를 다음과 같이 해결했다.

  1. 응답에 사용자 정의 헤더 추가 :

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. JavaScript 함수를 ajaxSuccess 이벤트에 바인딩하고 헤더가 있는지 확인합니다.

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    

난 그냥 전체 페이지에 대한 모든 아약스 요청에 래치 싶었어. @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에 바인드하여 성공 이외의 것을 얻을 수있다.

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

내가 발견 한 또 다른 해결책은 (특히 글로벌 동작을 설정하려는 경우 유용하다.) $.ajaxsetup() 메서드를 statusCode 속성 과 함께 사용하는 것이다. 지적 된 다른 사람들처럼 리디렉션 상태 코드 ( 3xx )를 사용하지 말고 4xx 를 사용하고 리디렉션 클라이언트 측을 처리하십시오.

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

400 을 처리하려는 상태 코드로 바꾸십시오. 이미 언급 한 401 Unauthorized 는 좋은 생각 일 수 있습니다. 나는 매우 특정 적이 400 때문에 400 을 사용하고 잘못된 로그인 자격 증명과 같은보다 구체적인 경우에 401 을 사용할 수 있습니다. 그래서 직접 리다이렉트하는 대신 백엔드는 세션이 타임 아웃되고 리디렉션 클라이언트 측을 처리 할 때 4xx 오류 코드를 리턴해야합니다. 백본과 같은 프레임 워크로조차도 완벽하게 작동합니다 .js


시험

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

로그인 페이지에 넣으십시오. 기본 페이지의 div에로드 된 경우 로그인 페이지가 나올 때까지 리디렉션됩니다. "#site"는 로그인 페이지를 제외한 모든 페이지에있는 div의 ID입니다.


어떤 사람들은 다음을 유용하게 사용할 수 있습니다.

나는 클라이언트가 인증 토큰없이 보내지는 모든 휴식 조치를 위해 로그인 페이지로 리디렉션되기를 원했습니다. 모든 휴식 액션은 Ajax 기반이기 때문에 Ajax 성공 기능을 처리하는 대신 로그인 페이지로 리디렉션하는 좋은 방법이 필요했습니다.

이것이 내가 한 일이다.

모든 Ajax 요청에서 내 서버는 Json 200 응답 "인증 필요"(클라이언트 인증이 필요한 경우)를 리턴합니다.

자바 (서버 측)의 간단한 예제 :

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map<String, Cookie> cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

내 자바 스크립트에서 다음 코드를 추가했습니다 :

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

그리고 그것에 관한 것입니다.


이 문제는 다음과 같이 해결되었습니다.

미들웨어를 추가하여 응답을 처리하고, Ajax 요청에 대한 리디렉션 인 경우 리다이렉트 URL로 응답을 일반 응답으로 변경하십시오.

class AjaxRedirect(object):
  def process_response(self, request, response):
    if request.is_ajax():
      if type(response) == HttpResponseRedirect:
        r = HttpResponse(json.dumps({'redirect': response['Location']}))
        return r
    return response

그런 다음 ajaxComplete에서 응답에 리디렉션이 포함되어 있으면 리디렉션이어야하므로 브라우저의 위치를 ​​변경하십시오.

$('body').ajaxComplete(function (e, xhr, settings) {
   if (xhr.status == 200) {
       var redirect = null;
       try {
           redirect = $.parseJSON(xhr.responseText).redirect;
           if (redirect) {
               window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
           }
       } catch (e) {
           return;
       }
   }
}

이 문제를 해결하는 더 좋은 방법은 기존 HTTP 프로토콜 응답 코드, 특히 401 Unauthorized 를 활용하는 것입니다.

여기 내가 어떻게 해결 했는가?

  1. 서버 측 : 세션이 만료되고 요청이 ajax 인 경우. 401 응답 코드 헤더를 보낸다.
  2. 클라이언트 측 : 아약스 이벤트에 바인딩

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });
    

IMO는 좀 더 일반적이고 사용자 정의 스펙 / 헤더를 새로 작성하지 않습니다. 또한 기존 Ajax 호출을 수정하지 않아도됩니다.

편집 : 아래 @ Rob의 코멘트, 401 (인증 오류에 대한 HTTP 상태 코드) 표시기가 있어야합니다. 자세한 내용은 403 Forbidden 대 401 Unauthorized HTTP responses 를 참조하십시오. 그 중 일부 웹 프레임 워크는 인증과 인증 오류 모두에 대해 403을 사용하므로 적절하게 적응하십시오. 롭 감사합니다.


저수준 $.ajax() 호출을 사용하십시오.

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

리디렉션을 시도해보십시오.

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

주어진 솔루션의 대부분은 추가 헤더 또는 부적절한 HTTP 코드를 사용하여 임시 해결책을 사용합니다. 그 해결책은 아마 작동하지만 조금 '해킹'을 느낄 것입니다. 나는 또 다른 해결책을 찾았다.

우리는 401 응답에서 (passiveRedirectEnabled = "true") 리디렉션하도록 구성된 WIF를 사용하고 있습니다. 리디렉션은 일반 요청을 처리 할 때 유용하지만 AJAX 요청에는 작동하지 않습니다 (브라우저는 302 / 리디렉션을 실행하지 않으므로).

global.asax에서 다음 코드를 사용하면 AJAX 요청에 대한 리디렉션을 비활성화 할 수 있습니다.

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

이렇게하면 AJAX 요청에 대해 401 개의 응답을 반환 할 수 있습니다. 그러면 AJAX 요청이 처리되어 페이지가 다시로드됩니다. 페이지를 새로 고침하면 401이 전송되어 WIF에서 처리하게됩니다 (WIF는 사용자를 로그인 페이지로 리디렉션합니다).

401 오류를 처리하는 예제 자바 스크립트 :

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});

@ 존과 @Arpad .com/a/8426947/4505142 및 @RobWinch link 의 답변을 사용하여 작업 해결 방법을 얻었습니다.link

나는 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에서 here 표시된대로 전역 AJAX 오류 처리기를 추가 here

  $( 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");
//            }
        });

나는 그것이 다른 사람들을 돕기를 바랍니다.

Update1 폼 로그인 설정에 옵션 (always-use-default-target = "true")을 추가해야한다는 것을 알았습니다. 이는 AJAX 요청이 만료 된 세션으로 인해 로그인 페이지로 리디렉션 된 후 Spring이 이전 AJAX 요청을 기억하고 로그인 후 자동으로 리다이렉트하므로 필요합니다. 이렇게하면 반환 된 JSON이 브라우저 페이지에 표시됩니다. 물론, 내가 원하는 것은 아닙니다.

Update2 를 사용하는 대신 always-use-default-target="true"requstCache에서 AJAX 요청을 차단하는 @RobWinch 예제를 사용하십시오. 이렇게하면 일반 링크가 로그인 후 원래 대상으로 리디렉션 될 수 있지만 AJAX는 로그인 후에 홈 페이지로 이동합니다.


난 장고 애플 리케이션에이 문제가 있었어 (면책 조항 : 나는 배울 수있는 tinkering 오전, 그리고 전문가는 절대로)입니다. 내가 원했던 것은 jQuery ajax를 사용하여 리소스에 DELETE 요청을 보내고 서버 측에서 삭제 한 다음 리디렉션을 (기본적으로) 홈페이지로 다시 보냅니다. HttpResponseRedirect('/the-redirect/')파이썬 스크립트에서 보내면 jQuery의 ajax 메서드가 302 대신 200을받는 것입니다. 따라서 내가 한 것은 300의 응답을 보내는 것입니다.

response = HttpResponse(status='300')
response['Location'] = '/the-redirect/' 
return  response

그런 다음 jQuery.ajax를 사용하여 클라이언트에서 요청을 보내거나 처리했습니다.

<button onclick="*the-jquery*">Delete</button>

where *the-jquery* =
$.ajax({ 
  type: 'DELETE', 
  url: '/resource-url/', 
  complete: function(jqxhr){ 
    window.location = jqxhr.getResponseHeader('Location'); 
  } 
});

어쩌면 300을 사용하는 것이 "옳은"것은 아니지만, 적어도 내가 원했던 것처럼 효과가있었습니다.

추신 : 이것은 SO의 모바일 버전에서 편집하는 데 큰 고통이었습니다. 어리석은 ISP는 내가 내 대답을 다했을 때 내 서비스 취소 요청을 바로 잡았다!


마지막으로, 사용자 지정을 추가하여 문제를 해결합니다 HTTP Header. 서버 측의 모든 요청에 ​​대한 응답이 있기 바로 전에 응답 헤더에 현재 요청 된 URL을 추가합니다.

서버의 내 응용 프로그램 유형이 Asp.Net MVC있으며이를 수행 할 수있는 좋은 기회가 있습니다. 에서 Global.asax내가 구현 Application_EndRequest그래서 이벤트를 :

    public class MvcApplication : System.Web.HttpApplication
    {

    //  ...
    //  ...

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
        }

    }

그것은 나를 위해 완벽하게 작동합니다! 이제 모든 응답에는 상태 에 의한 메서드 결과로 나온 JQuery $.post요청 url및 기타 응답 헤더가 있습니다 .POST302303

다른 중요한 점은 서버 측이나 클라이언트 측에서 코드를 수정할 필요가 없다는 것입니다.

다음은 이러한 오류, 메시지 및 ...과 같은 게시 조치의 다른 정보에 대한 액세스 권한을 얻는 것입니다.

나는 이것을 게시했다, 아마도 누군가를 도왔다 :)


프로토 타입을 보내 XMLHttpRequest를 연결할 수도 있습니다. 이것은 하나의 핸들러로 모든 send (jQuery / dojo / etc)에서 작동합니다.

500 페이지의 만료 된 오류를 처리하기 위해이 코드를 작성했지만 200 리디렉션을 트랩하는 것만으로도 작동합니다. readyState의 의미에 대해 XMLHttpRequest onreadystatechange 에서 wikipedia 항목을 준비하십시오 .

// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.send = function() {
  //console.dir( this );

  this.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
      try {
        document.documentElement.innerHTML = this.responseText;
      } catch(error) {
        // IE makes document.documentElement read only
        document.body.innerHTML = this.responseText;
      }
    }
  };

  oldXMLHttpRequestSend.apply(this, arguments);
}




redirect