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


1368

내가 사용하고 $.post()Ajax를 사용하여 서블릿을 호출 한 다음은 HTML 조각을 결과하는 것은 교체 사용하여 div사용자의 현재 페이지의 요소를. 그러나 세션 시간이 초과되면 서버는 리디렉션 지시문을 보내 사용자를 로그인 페이지로 보냅니다. 이 경우 jQuery는 div요소를 로그인 페이지의 내용으로 대체 하여 사용자의 눈이 드문 장면을 목격하게합니다.

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


1
(답변이 아님)-jquery 라이브러리를 편집하고 각 XHR에 로그인 페이지 확인을 추가하여 과거 에이 작업을 수행했습니다. 업그레이드 할 때마다 수행해야하므로 최상의 솔루션은 아니지만 문제를 해결합니다.
Sugendran


HttpContext.Response.AddHeaderajaxsetup 성공에 체크 길을 가야하는 것입니다
LCJ

4
서버가 401을 반환 할 수없는 이유는 무엇입니까? 이 경우 전역 $ .ajaxSetup을 사용하고 상태 코드를 사용하여 페이지를 리디렉션 할 수 있습니다.
Vishal

1
이 링크 doanduyhai.wordpress.com/2012/04/21/… 올바른 솔루션을 제공합니다
pappu_kutty

답변:


697

이 질문을 읽고 브라우저가 리디렉션을 투명하게 처리하지 않도록 응답 HTTP 상태 코드 를 278로 설정하는 것과 관련하여 언급 된 접근 방식을 구현했습니다 . 이것이 효과가 있었지만 약간의 해킹이기 때문에 약간 불만족했습니다.

더 파고 들자, 나는이 접근법을 버리고 JSON을 사용했습니다 . 이 경우 AJAX 요청에 대한 모든 응답은 상태 코드 200을 가지며 응답 본문에는 서버에서 구성된 JSON 오브젝트가 포함됩니다. 그런 다음 클라이언트의 JavaScript는 JSON 객체를 사용하여 필요한 작업을 결정할 수 있습니다.

나는 당신과 비슷한 문제가있었습니다. 가능한 두 가지 응답이있는 AJAX 요청을 수행합니다. 하나 는 브라우저를 새 페이지로 리디렉션 하고 다른 하나 는 현재 페이지의 기존 HTML 양식을 새 페이지로 바꿉니다 . 이를 수행하는 jQuery 코드는 다음과 같습니다.

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        } else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

JSON 객체 "data"는 서버에서 2 개의 멤버 ( data.redirect및) 를 갖도록 구성됩니다 data.form. 나는이 접근법이 훨씬 더 낫다는 것을 알았습니다.


58
stackoverflow.com/questions/503093/ 의 솔루션에 명시된 바와 같이 window.location.replace (data.redirect);를 사용하는 것이 좋습니다. window.location.href = data.redirect보다;
Carles Barrobés

8
액션에 HTTP 코드를 사용하는 것이 좋지 않은 이유는 무엇입니까? 예를 들어 307 코드는 HTTP Temporary Redirect?
Sergei Golos

15
@ Sergei Golos 이유는 HTTP 리디렉션을 수행하면 실제로 리디렉션이 ajax success 콜백에 도착하지 않기 때문입니다. 브라우저는 리디렉션 대상의 내용으로 200 코드를 전달하는 리디렉션을 처리합니다.
미겔 실바

2
서버 코드는 어떻게 생겼습니까? data.form 및 data.redirect의 반환 값을 갖습니다. 기본적으로 리디렉션을 넣을지 어떻게 결정합니까?
Vnge

6
이 답변은 서버에서 어떻게 수행되는지 보여 주면 더 도움이 될 것입니다.
Pedro Hoehl Carvalho

243

이 문제를 다음과 같이 해결했습니다.

  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 = '/';
        }
    });

6
정말 멋진 솔루션입니다. 원 스톱 솔루션이라는 아이디어가 마음에 듭니다. 403 상태를 확인해야하지만 본문에 ajaxSuccess 바인드를 사용할 수 있습니다 (실제로 찾고 있던 것입니다). 감사합니다.
Bretticus

4
방금이 작업을 수행하고 $ .get () 함수를 사용하고 200 이외의 다른 상태가 실행되지 않은 ajaxComplete가 필요하다는 것을 알았습니다. 사실, 아마도 ajaxError에 바인딩했을 수 있습니다. 자세한 내용은 아래 답변을 참조하십시오.
Bretticus

2
많은 경우에 문제가 없지만, 프레임 워크가 인증을 처리한다면 어떨까요?
jwaliszko

13
나는 헤더 접근 방식을 좋아하지만 @ mwoods79와 같이 리디렉션 할 위치에 대한 지식은 복제해서는 안된다고 생각합니다. 부울 대신 REDIRECT_LOCATION 헤더를 추가하여 문제를 해결했습니다.
rintcius

3
리디렉션 후 응답에 헤더를 설정하십시오. 이 페이지의 다른 답변에서 설명한 것처럼 리디렉션은 ajaxSucces 핸들러에 투명 할 수 있습니다. 따라서 로그인 페이지의 GET 응답에 헤더를 포함 시켰습니다 (결국 내 시나리오에서 ajaxSuccess 만 트리거했습니다).
sieppl

118

브라우저가 301 및 302 응답을 올바르게 처리하지 않습니다. 그리고 실제로이 표준은 Ajax Library 공급 업체에게는 큰 골칫거리 인 "투명하게"처리해야한다고 말합니다. 에서 RA-아약스 우리는 투명하게 처리하는 서버에서 리디렉션 할 HTTP 응답 상태 코드 278 (그냥 "사용되지 않는"성공 코드)를 사용하여 강제로했다 ...

이것은 정말로 나를 귀찮게하며, 여기 누군가 W3C에 "풀"이 있으면 W3C에 301 및 302 코드를 직접 처리해야한다는 것을 W3C에 알릴 수 있음을 감사드립니다 ...! ;)


3
한 번의 이동으로 278이 공식 HTTP 스펙과 분리되어야합니다.
Chris Marisic 2016 년

2
이미 투명하게 처리하지 않습니까? 자원이 이동 한 경우 투명하게 처리한다는 것은 제공된 URL에서 요청을 반복하는 것을 의미합니다. 이것이 XMLHttpRequest API를 사용할 것으로 예상되는 것입니다.
Philippe Rathé

@ PhilippeRathé 동의합니다. 투명하게 다루는 것이 내가 원하는 것입니다. 그리고 왜 그것이 나쁜 것으로 간주되는지 모르겠습니다.
smwikipedia

@smwikipedia 페이지를 리디렉션하지 않고 메인 섹션에서 마크 업 리디렉션을 정렬합니다.
cmc

여기 누군가 W3C에 "풀 (pull)"이 있으면 W3C에 301 및 302 코드를 직접 처리해야한다는 사실을 W3C에 알릴 수 있다는 점에 감사드립니다. ;)
Tommy

100

결국 구현 된 솔루션은 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을 반환했기 때문에 효과적이었습니다. 또한 로그인 페이지로 리디렉션하기 만하면됩니다.


4
cbWrapper (funct) {return function (data) {if ($ ( "# myForm", data) .size ()> 0) top.location.href = "login"; 다른 funct (데이터); }}. 그런 다음 .post를 호출 할 때 cbWrapper (myActualCB) 만 필요합니다. 예, 주석 코드는 엉망이지만 주목해야합니다 :)
Simen Echholt

당신이 크기 대신에 여기 .length 사용할 수 있도록 크기는 감가 상각
선일

66

레몬이 약간 꼬인 Timmerz의 방법이 마음에 듭니다. 당신이 이제까지 반환받을 경우 의 contentType텍스트 / HTML을 당신이 예상 할 때 JSON을 , 당신이 가장 가능성이 리디렉션되고있다. 내 경우에는 단순히 페이지를 다시로드하면 로그인 페이지로 리디렉션됩니다. 아, 그리고 jqXHR 상태가 200인지 확인하십시오. 오류 기능에 있기 때문에 어리석은 것처럼 보입니다. 그렇지 않으면 합법적 인 오류 사례로 인해 반복적 인 재로드 (강제)가 발생합니다.

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

});

1
Brian에게 감사를 표합니다. 간단한 "콘텐츠 유형"검사 대신 어떤 URL / 페이지가 리디렉션되는지 비교하는 것과 같은보다 안전한 검사가 필요한 경우에도 귀하의 답변이 제 시나리오에 가장 적합했습니다. jqXHR 객체에서 리디렉션중인 페이지를 찾을 수 없습니다.
Johnny

상태 401을 확인한 다음 리디렉션합니다. 챔피언처럼 작동합니다.
Eric

55

저수준 $.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 코드가 3xx임을 감지하면 브라우저 리디렉션을 어떻게 강제합니까? 내 목표는 사용자의 세션이 만료되었음을 알리는 것이 아니라 사용자를 리디렉션하는 것입니다.
Elliot Vargas

4
Btw, $ .ajax ()는 매우 낮은 수준은 아닙니다. $ .ajax 및 모든 옵션보다 훨씬 간단한 $ .get, $ .post 등이 있기 때문에 jQuery 측면에서 저수준입니다.
까지

16
오 소년! 답변을 "수락 취소"해야해서 죄송합니다. 여전히 도움이됩니다. 즉, 리디렉션은 XMLHttpRequest에 의해 자동으로 관리되므로 리디렉션 후 항상 200 상태 코드를 얻습니다 (한숨!). HTML을 파싱하고 마커를 찾는 것과 같은 불쾌한 일을해야한다고 생각합니다.
Elliot Vargas

1
세션이 서버에서 끝난다고해서 서버가 다른 SESSIONID를 전송한다는 의미는 아닙니다. 우리는 그것을 감지 할 수 없었습니까?
Salamander2007

10
참고 : 리디렉션에는 작동하지 않습니다. ajax는 새 페이지로 이동하여 상태 코드를 반환합니다.

33

누군가에게 도움이 될 수 있으므로 내 접근 방식을 공유하고 싶었습니다.

기본적으로 사용자 이름 표시와 같은 인증 항목을 처리하는 JavaScript 모듈과 로그인 페이지로리디렉션을 처리하는 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 리디렉션 응답을 캡처 할 수 없으므로 LoginPage로그인 페이지 자체의 URL이 포함 된 헤더를 로그인 페이지에 추가했습니다 . 모듈에서 이제 헤더를 듣고 리디렉션을 수행합니다.

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

... 그리고 그것은 매력처럼 작동합니다 :). 왜 LoginPage헤더에 URL을 포함시키는 지 궁금 할 것입니다 ... 기본적으로 객체 GET에서 자동 위치 리디렉션으로 인한 URL을 결정하는 방법을 찾지 못했기 때문에 xhr...


+1-사용자 정의 헤더는로 시작해야 X-하므로 더 나은 헤더를 사용하는 것이 좋습니다 X-LoginPage: http://example.com/login.
uınbɐɥs

6
@ShaquinTrifonoff 더 이상은 없습니다. 2011 년 6 월 에 ITEF 문서가 더 이상 사용되지 않음을 제안하고 실제로 2012 년 6 월 에 사용자 지정 헤더에 더 이상 접두사를 붙이지 않아야한다고 공식화 하지 않았기 때문에 X- 접두사를 사용하지 않았습니다 X-.
Juri

우리는 또한 ISA 서버를 가지고 있으며 방금 같은 문제가 발생했습니다. 코드로 해결하는 대신 kb2596444 의 지침을 사용하여 리디렉션을 중지하도록 ISA를 구성했습니다.
scott stone

29

나는이 주제가 오래되었다는 것을 알고 있지만 여기서 찾아서 이전에 설명했던 또 다른 접근법을 제시 할 것이다 . 기본적으로 나는 WIF와 함께 ASP.MVC를 사용하고 있습니다 (그러나 이것은이 주제의 맥락에서 실제로 중요하지 않습니다-어떤 프레임 워크를 사용하든 대답은 적절합니다. 단서는 변경되지 않습니다. 단서가 변경되지 않음-아약스 요청을 수행하는 동안 인증 실패와 관련된 문제 처리) ) .

아래에 표시된 접근 방식은 모든 ajax 요청에 즉시 적용 할 수 있습니다 (beforeSend 이벤트를 분명히 재정의하지 않은 경우).

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

모든 아약스 요청이 수행되기 전에 CheckPulse 메소드가 호출됩니다 (가장 간단한 컨트롤러 메소드).

[Authorize]
public virtual void CheckPulse() {}

사용자가 인증되지 않은 경우 (토큰이 만료 된 경우) 해당 방법에 액세스 할 수 없습니다 ( Authorize 속성에 ). 프레임 워크는 인증을 처리하므로 토큰이 만료되는 동안 http 상태 302가 응답에 적용됩니다. 브라우저가 302 응답을 투명하게 처리하지 못하게하려면 Global.asax에서이를 감지하고 응답 상태 (예 : 200 OK)를 변경하십시오. 또한 헤더를 추가하면 이러한 응답을 특별한 방법으로 (나중에 클라이언트 측에서) 처리하도록 지시합니다.

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

마지막으로 클라이언트 측에서 이러한 사용자 정의 헤더를 확인하십시오. 존재하는 경우-로그온 페이지로의 전체 경로 재 지정을 수행해야합니다 (제 경우에는 window.location프레임 워크가 자동으로 처리하는 요청의 url로 대체 됨).

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}

EndRequest 이벤트 대신 PostAuthenticateRequest 이벤트를 사용하여 문제를 해결했습니다.
Frinavale

@JaroslawWaliszko 마지막 응답에 잘못된 이벤트를 붙여 넣었습니다! 나는 PreAuthendcateRequest가 아닌 PreSendRequestHeaders 이벤트를 의미했습니다! >> blush << 내 실수를 지적 해 주셔서 감사합니다.
Frinavale

@JaroslawWaliszko WIF를 사용할 때 AJAX 요청에 대해 401 응답을 반환하고 자바 스크립트가 처리하도록 할 수 있습니다. 또한 모든 302 인증이 필요하지만 모든 경우에 사실이 아닐 수도 있다고 가정합니다. 관심있는 사람이 있으면 답변을 추가했습니다.
Rob

26

이 문제를 처리하는 더 좋은 방법은 기존의 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 이것은 더 일반적이며 새로운 사용자 지정 사양 / 헤더를 작성하지 않습니다. 또한 기존 아약스 호출을 수정하지 않아도됩니다.

편집 : 아래 @Rob의 의견에 따라 401 (인증 오류의 HTTP 상태 코드)이 표시되어야합니다. 자세한 내용은 403 Forbidden vs 401 Unauthorized HTTP 응답 을 참조하십시오. 그 말로 일부 웹 프레임 워크는 인증 및 권한 부여 오류 모두에 403을 사용하므로 적절하게 조정하십시오. 고마워 Rob.


나는 같은 접근법을 사용합니다. jQuery는 실제로 403 오류 코드에서 ajaxSuccess를 호출합니까? 실제로 ajaxError 부분 만 필요하다고 생각합니다.
Marius Balčytis

24

이 문제를 다음과 같이 해결했습니다.

응답을 처리하기 위해 미들웨어를 추가하십시오. 응답이 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;
       }
   }
}

20

나에게 맞는 간단한 솔루션이 있으며 서버 코드를 변경할 필요가 없습니다.

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "<html>" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

html 태그가 있는지 확인하지만 indexOf를 변경하여 로그인 페이지에 존재하는 고유 한 문자열을 검색 할 수 있습니다 ...


이것은 나를 위해 작동하지 않는 것 같습니다. 아약스 호출로 정의 된 함수를 계속 호출합니다. 성공 방법을 재정의하지 않는 것과 같습니다.
adriaanp

적어도 더 이상 나를 위해 작동하지 않는 것 같습니다. 가능한 설명 : stackoverflow.com/a/12010724/260665
Raj Pawan Gumdal

20

내가 찾은 또 다른 해결책은 (특히 글로벌 동작을 설정하려는 경우 특히 유용합니다) $.ajaxsetup()메소드를 statusCodeproperty 와 함께 사용하는 것입니다 . 다른 사람들이 지적했듯이 리디렉션 상태 코드 ( 3xx)를 사용하지 말고 상태 코드를 사용 4xx하여 리디렉션 클라이언트 측을 처리하십시오.

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

400처리하려는 상태 코드로 교체하십시오 . 이미 언급했듯이 401 Unauthorized좋은 생각 일 수 있습니다. 나는 400매우 401구체적이지 않기 때문에를 사용하고 더 구체적인 경우 (잘못된 로그인 자격 증명과 같은)를 사용할 수 있습니다 . 따라서 4xx세션을 시간 초과하고 클라이언트 측 리디렉션을 처리하면 백엔드에서 직접 리디렉션하는 대신 오류 코드 가 반환 됩니다. backbone.js와 같은 프레임 워크에서도 완벽하게 작동합니다.


페이지에서 기능을 언급 할 곳은?
Vikrant

@Vikrant 질문을 올바르게 이해하면 jQuery 가로 드 된 직후와 실제 요청을 수행하기 전에 함수를 호출 할 수 있습니다.
morten.c

19

주어진 솔루션의 대부분은 여분의 헤더 또는 부적절한 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 응답을 리턴 할 수 있으며, 그러면 페이지를 다시로드하여 Javascript가 처리 할 수 ​​있습니다. 페이지를 다시로드하면 401이 발생하고 W401이 처리합니다 (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);
    }
});

좋은 해결책입니다. 감사.
DanMan

19

이 문제는 ASP.NET MVC RedirectToAction 메서드를 사용하여 나타날 수 있습니다. div에 응답을 표시하는 양식을 방지하려면 $ .ajaxSetup으로 들어오는 응답에 대해 일종의 ajax 응답 필터를 수행 하면 됩니다. 응답에 MVC 리디렉션이 포함 된 경우 JS 측에서이 표현식을 평가할 수 있습니다. 아래 JS의 예제 코드 :

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

데이터가 "window.location = '/ Acount / Login'"인 경우 필터는이를 감지하여 데이터를 표시하지 않고 리디렉션하도록 평가합니다.


data응답 본문 또는 헤더에 있습니까?
GMsoF

16

블라디미르 프루트 니 코프와 토마스 한센의 말을 종합 해보자.

  • 서버 측 코드가 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); 
});

13
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>

12

시험

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

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


12

Spring Security를 ​​사용하는 경우 사람들에게 답이 효과가있는 것처럼 보이지만 LoginUrlAuthenticationEntryPoint를 확장하고 AJAX를보다 강력하게 처리하기 위해 특정 코드를 추가하는 것을 발견했습니다. 대부분의 예제는 인증 실패뿐만 아니라 모든 리디렉션을 차단 합니다 . 이것은 내가 작업하는 프로젝트에 바람직하지 않았습니다. 실패한 AJAX 요청을 캐시하지 않으려면 ExceptionTranslationFilter를 확장하고 "sendStartAuthentication"메소드를 대체하여 캐싱 단계를 제거해야 할 수도 있습니다.

예 AjaxAwareAuthenticationEntryPoint :

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (isAjax(request)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
        } else {
        super.commence(request, response, authException);
        }
    }

    public static boolean isAjax(HttpServletRequest request) {
        return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

출처 : 1 , 2


3
다운 유권자가 다운 투표를 한 이유를 설명하면 도움이 될 것입니다. 이 솔루션에 나쁜 점이 있으면 실수로 배우고 싶습니다. 감사.
John

Spring을 사용하고 JSF를 사용하는 경우 다음 사항도 확인하십시오. ( "partial / ajax"). equalsIgnoreCase (request.getHeader ( "faces-request"));
J Slick

(1) 오류 응답을 감지하기위한 필수 클라이언트 측 모드; (2) 사용자 정의 된 LoginUrlAuthenticationEntryPoint를 필터 체인에 추가하기 위해 필수 구성을 Spring 구성에 추가하십시오.
J Slick

귀하의 답변은 @Arpad의 답변과 유사합니다. 그것은 나를 위해 일했다. 스프링 시큐리티 사용하기 3.2.9. stackoverflow.com/a/8426947/4505142
Darren Parker

11

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

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

10

@Steg에 설명 된대로 문제를 다시 인용하겠습니다.

나는 당신과 비슷한 문제가있었습니다. 가능한 두 가지 응답이있는 ajax 요청을 수행합니다. 하나는 브라우저를 새 페이지로 리디렉션하고 다른 하나는 현재 페이지의 기존 HTML 양식을 새 페이지로 바꿉니다.

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

새로운 HTTP 표준은 새로운 상태 코드를 사용하는 것이라고 생각합니다. 의미 : 현재 301/302브라우저 에게이 요청 의 내용을 new로 가져 오도록 지시합니다 location.

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

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

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

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

이것이 당신에게 의미가 있습니까?


7

일부는 아래가 유용 할 수 있습니다.

인증 토큰없이 전송 된 나머지 작업에 대해 클라이언트를 로그인 페이지로 리디렉션하기를 원했습니다. 모든 휴식 조치가 Ajax 기반이기 때문에 Ajax 성공 함수를 처리하는 대신 로그인 페이지로 리디렉션하는 좋은 일반적인 방법이 필요했습니다.

이것이 내가 한 일입니다.

모든 Ajax 요청에서 내 서버는 "클라이언트 인증이 필요한 경우"Json 200 응답 "필요함"을 반환합니다.

Java의 간단한 예 (서버 측) :

@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());
        }
    }
}

내 Javascript에서 다음 코드를 추가했습니다.

$.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);
        }
    };      
});

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


5

서블릿 response.setStatus(response.SC_MOVED_PERMANENTLY); 에서 리디렉션에 필요한 '301'xmlHttp 상태를 보내야합니다 ...

그리고 $ .ajax 함수에서 함수를 사용해서는 안됩니다 .toString()..., 그냥

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

문제는 유연성이 뛰어나지 않기 때문에 리디렉션하려는 위치를 결정할 수 없습니다.

서블릿을 통한 리디렉션이 가장 좋은 방법이어야합니다. 하지만 여전히 올바른 방법을 찾을 수 없습니다.


5

방금 전체 페이지에 대한 모든 아약스 요청을 걸고 싶었습니다. @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");
}

1
후자는 문제를 해결하는 다른 오류를 숨길 것입니다
Tim Abell

1
403은 사용자가 인증되지 않았 음을 의미하지 않으며, (아마 인증 된) 사용자에게 요청 된 리소스를 볼 수있는 권한이 없음을 의미합니다. 로그인 페이지로 리디렉션해서는 안됩니다
Rob

5

값을 전달하려면 세션 변수를 설정하고 예를 들어 액세스 할 수 있습니다 : jsp에서 다음을 작성할 수 있습니다

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

그런 다음이 임시 값을 자바 스크립트 변수에 저장하고 재생할 수 있습니다


5

헤더 솔루션으로 성공하지 못했습니다 .ajaxSuccess / ajaxComplete 메소드에서 선택되지 않았습니다. Steg의 답변을 사용자 정의 응답과 함께 사용했지만 JS 측을 일부 수정했습니다. 표준 $.get$.post메소드를 사용할 수 있도록 각 함수에서 호출하는 메소드를 설정했습니다 .

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

사용중인 예 ...

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}

5

마지막으로 custom을 추가하여 문제를 해결합니다 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 $.posti 의 모든 응답 에서 요청한 상태 와 상태 별 메소드의 url결과로 오는 다른 응답 헤더가 있습니다 .POST302303 ....

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

다음은 오류, 메시지 등의 사후 조치에 대한 다른 정보에 액세스 할 수있는 기능입니다.

나는 이것을 누군가에게 도울 수 있었다 :)


4

나는 내가 고민하고있는 장고 응용 프로그램 에서이 문제를 겪고 있었다 (면책 조항 : 나는 배우고 싶고 전문가가 아니다). 내가하고 싶었던 것은 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는 내가 대답을 마치면 바로 서비스 취소 요청을했습니다!


4

XMLHttpRequest 전송 프로토 타입을 후크 할 수도 있습니다. 이것은 하나의 핸들러로 모든 전송 (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);
}

2

또한 사용자를 지정된 헤더 URL로 리디렉션하려고 할 것입니다. 마지막으로 다음과 같이 보입니다 :

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD : 죄송합니다. 동일한 작업을 수행하지만 작동하지 않습니다. 이 일을 내가 찾을 때 당신에게 해결책을 보여줄 것입니다.


4
이 답변은 저자가 스스로 작동한다고 말하면서 삭제해야합니다. 나중에 작업 솔루션을 게시 할 수 있습니다. -1
TheCrazyProgrammer 2012

아이디어는 좋지만 문제는 "위치"헤더가 전달되지 않는다는 것입니다.
Martin Zvarík

내 편집이 거부되었으므로 "var xmlHttp = $ .ajax ({...."변수를 사용할 수 없습니다 ... 다음을 사용하십시오. console.log (xmlHttp.getAllResponseHeaders ()) ;
마틴 Zvarík

2

@John 및 @Arpad 링크 및 @RobWinch 링크 의 답변을 사용하여 작동하는 솔루션이 있습니다.

Spring Security 3.2.9 및 jQuery 1.10.2를 사용합니다.

AJAX 요청에서만 4XX 응답을 발생 시키도록 Spring 클래스를 확장하십시오.

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 옵션 (항상 use-default-target = "true")을 form-login 구성에 추가해야한다는 것을 알았습니다. 세션이 만료되어 AJAX 요청이 로그인 페이지로 리디렉션 된 후 Spring은 이전 AJAX 요청을 기억하고 로그인 후 자동 리디렉션합니다. 그러면 반환 된 JSON이 브라우저 페이지에 표시됩니다. 물론, 내가 원하는 것은 아닙니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.