Bir jQuery Ajax çağrısından sonra yönlendirme isteği nasıl yönetilir


1369

Ben kullanıyorum $.post()Ajax kullanarak servlet çağırmak ve daha sonra HTML parçası sonuçlanan bir yerine kullanarak divkullanıcının geçerli sayfasında elemanı. Ancak oturum zaman aşımına uğrarsa, sunucu kullanıcıyı oturum açma sayfasına göndermek için bir yönlendirme yönergesi gönderir. Bu durumda, jQuery divöğeyi oturum açma sayfasının içeriğiyle değiştirerek kullanıcının gözlerini nadir bulunan bir sahneye şahit olmaya zorlar.

JQuery 1.2.6 ile bir Ajax çağrısından bir yönlendirme yönergesini nasıl yönetebilirim?


1
(böyle bir cevap değil) - Bunu geçmişte jquery kütüphanesini düzenleyerek ve her XHR'deki giriş sayfası için bir kontrol ekleyerek yaptım. En iyi çözüm değil, çünkü her yükselttiğinizde yapılması gerekiyordu, ancak sorunu çözüyor.
Sugendran


HttpContext.Response.AddHeaderAjaxsetup sucess de ve çek gitmek yoludur
LCJ

4
Sunucu neden 401 iadesi yapamıyor? Bu durumda, genel bir $ .ajaxSetup sahibi olabilir ve sayfayı yeniden yönlendirmek için durum kodunu kullanabilirsiniz.
Vishal

1
bu bağlantı doanduyhai.wordpress.com/2012/04/21/… bana doğru çözümü veriyor
pappu_kutty

Yanıtlar:


697

Bu soruyu okudum ve tarayıcının yönlendirmeleri şeffaf bir şekilde işlemesini önlemek için yanıt HTTP durum kodunun 278 olarak ayarlanmasıyla ilgili olarak belirtilen yaklaşımı uyguladım . Bu işe yaramış olsa da, biraz hack olduğu için biraz memnun kalmadım.

Daha etrafında kazma sonra ben bu yaklaşımı ve kullanılmış hendeğe JSON . Bu durumda, AJAX isteklerine verilen tüm yanıtlar 200 durum koduna sahiptir ve yanıtın gövdesi sunucuda oluşturulmuş bir JSON nesnesi içerir. İstemci üzerindeki JavaScript daha sonra ne yapması gerektiğine karar vermek için JSON nesnesini kullanabilir.

Sizinkine benzer bir sorunum vardı. 2 olası yanıtı olan bir AJAX isteği gerçekleştiriyorum: biri tarayıcıyı yeni bir sayfaya yönlendiriyor ve biri geçerli sayfadaki mevcut bir HTML formunu yenisiyle değiştiriyor . Bunu yapmak için jQuery kodu şuna benzer:

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

"Data" JSON nesnesi sunucuda 2 üyeye sahip olacak şekilde oluşturulur: data.redirectve data.form. Bu yaklaşımın çok daha iyi olduğunu gördüm.


58
Stackoverflow.com/questions/503093/… içindeki çözümde belirtildiği gibi window.location.replace (data.redirect) kullanmak daha iyidir; window.location.href = data.redirect;
Carles Barrobés

8
İşlemde HTTP kodları kullanmanın daha iyi olmamasının herhangi bir nedeni. Örneğin, HTTP Geçici Yönlendirme olan 307 kodu?
Sergei Golos

15
@Sergei Golos bunun nedeni, bir HTTP yönlendirmesi yaparsanız, yönlendirme aslında hiçbir zaman ajax başarı geri aramasına ulaşmaz. Tarayıcı, yönlendirmeyi, yönlendirmenin hedefinin içeriğiyle birlikte 200 kod göndererek işler.
Miguel Silva

2
sunucu kodu nasıl görünürdü? data.form ve data.redirect dönüş değerine sahip olmak. temelde içine bir yönlendirme koyup koymadığımı nasıl belirlerim?
Vnge

6
Bu yanıt, sunucuda nasıl yapıldığını gösterseydi daha yararlı olurdu.
Pedro Hoehl Carvalho

243

Bu sorunu şu şekilde çözdüm:

  1. Yanıtı özel bir başlık ekleme:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
  2. ajaxSuccessEtkinliğe bir JavaScript işlevi bağlama ve başlığın var olup olmadığını kontrol etme:

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

6
Ne harika bir çözüm. Tek elden çözüm fikrini seviyorum. Bir 403 durumu kontrol etmek gerekir, ama bu (gerçekten ne aradığını budur.) Bunun için vücut üzerinde ajaxSuccess bağlama kullanabilirsiniz.
Bretticus

4
Ben sadece bunu yaptım ve nerede $ .get () işlevini kullanıyordu ve 200 dışında herhangi bir durum ateş değildi ajaxComplete gerekli bulundu. Aslında, bunun yerine muhtemelen ajaxError'a bağlı olabilirdim. Daha fazla ayrıntı için aşağıdaki cevabıma bakın.
Bretticus

2
Birçok durumda sorun olmaz, ancak çerçeveniz yetkilendirmeyi ele alıyorsa ne olur?
jwaliszko

13
Üstbilgi yaklaşımını seviyorum ama aynı zamanda - @ mwoods79 gibi - nereye yeniden yönlendirileceği bilgisinin çoğaltılmaması gerektiğini düşünüyorum. Bir boolean yerine REDIRECT_LOCATION üstbilgisi ekleyerek bunu çözdüm.
rintcius

3
Yönlendirmeden SONRA tepkisi başlığa ayarlamaya özen gösterin. Bu sayfadaki diğer yanıtlarda açıklandığı gibi, yönlendirme ajaxSucces işleyicisine şeffaf olabilir. Bu yüzden giriş sayfasının GET yanıtına üstbilgiyi dahil ettim (sonunda ve sadece senaryomda ajaxSuccess'i tetikledi).
sieppl

118

Hiçbir tarayıcı 301 ve 302 yanıtlarını doğru şekilde işlemez. Ve aslında standart, Ajax Kütüphane satıcıları için bir MASSIVE baş ağrısı olan "şeffaf bir şekilde" başa çıkmaları gerektiğini bile söylüyor. In Ra-Ajax biz şeffaf idare sunucudan yönlendirir için HTTP yanıt durum kodu 278 (sadece bazı "kullanılmamış" başarı kodu) kullanarak içine zorlandı ...

Bu gerçekten beni kızdırıyor ve eğer burada birisi W3C'de biraz "çekiyorsa" W3C'ye gerçekten kendimiz 301 ve 302 kodlarını işlememiz gerektiğini bilmenizi isteriz ...! ;)


3
Bir hamle için 278'in resmi HTTP Spesifikasyonundan ayrı olması gerekir.
Chris Marisic

2
Onları şeffaf bir şekilde ele almıyor mu? Bir kaynak taşındıysa, saydam bir şekilde ele alınması, sağlanan URL'deki isteğin tekrarlanması anlamına gelir. XMLHttpRequest API kullanarak ne beklenir.
Philippe Rathé

@ PhilippeRathé Kabul ediyorum. Şeffaf bir şekilde kullanmak istediğim şey. Ve neden kötü sayıldığını bilmiyorum.
smwikipedia

@ smwikipedia Sayfayı yeniden yönlendirmeden ana bölümdeki işaretlemenin yönlendirmesini ayarlamak için.
cmc

burada birisi W3C bazı "çekme" varsa W3C gerçekten 301 ve 302 kodları kendimiz işlemek gerektiğini bilmek izin verebilir takdir ediyorum ...! ;)
Tommy.

100

Sonunda uygulanan çözüm, Ajax çağrısının geri çağırma işlevi için bir sarıcı kullanmak ve bu sarıcıda, döndürülen HTML yığınında belirli bir öğenin varlığını kontrol etmekti. Öğe bulunursa, sarıcı bir yönlendirme gerçekleştirdi. Değilse, sarıcı çağrıyı gerçek geri arama işlevine yönlendirdi.

Örneğin, sarma fonksiyonumuz şöyleydi:

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

Sonra, Ajax çağrısı yaparken şöyle bir şey kullandık:

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

Bu bizim için işe yaradı çünkü tüm Ajax çağrıları her zaman sayfanın bir parçasını değiştirmek için kullandığımız bir DIV öğesinin içine HTML döndürdü. Ayrıca, yalnızca giriş sayfasına yönlendirmemiz gerekiyordu.


4
Bunun cbWrapper (işlev) {return function (data) {if ($ ("# myForm", data) .size ()> 0) top.location.href = "login" olarak kısaltılabileceğini unutmayın. başka işlev (veri); }}. Daha sonra yalnızca .post çağrılırken cbWrapper (myActualCB) gerekir. Evet, yorumlarda kod bir karışıklık ama dikkat edilmelidir :)
Simen Echholt

boyutu amortismana tabi tutulur, böylece boyutu yerine .length kullanabilirsiniz
sunil

66

Timmerz'in yöntemini hafifçe limonla seviyorum. Hiç geri alırsanız contentType ait metin / html bekliyorsanız zaman JSON , büyük olasılıkla yönlendiriliyorsunuz. Benim durumumda, sadece sayfayı yeniden yüklüyorum ve giriş sayfasına yönlendiriliyor. Oh, ve hata fonksiyonunda olduğunuz için jqXHR durumunun aptalca görünen 200 olduğunu kontrol edin, değil mi? Aksi takdirde, meşru hata durumları yinelemeli bir yeniden yüklemeyi zorlar (ayy)

$.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
çok teşekkürler Brian, cevabım senaryom için en iyisiydi, ancak basit "içerik türü" kontrolü yerine hangi url / sayfanın yönlendirildiğini karşılaştırmak gibi daha güvenli bir kontrol olup olmadığını istiyorum. Hangi sayfanın jqXHR nesnesinden yönlendirildiğini bulamadım.
Johnny

401 durumunu kontrol ettim ve sonra yeniden yönlendirdim. Bir şampiyon gibi çalışır.
Eric

55

Düşük seviye $.ajax()aramayı kullanın :

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

Yönlendirme için bunu deneyin:

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

Düşük seviyeli şeylerden kaçınmaya çalışıyordum. Her neyse, açıkladığım gibi bir şey kullandığımı varsayalım, HTTP kodunun 3xx olduğunu algıladıktan sonra tarayıcı yönlendirmesini nasıl zorlarım? Amacım, yalnızca oturumunun sona erdiğini duyurmak için değil, kullanıcıyı yeniden yönlendirmektir.
Elliot Vargas

4
Btw, $ .ajax () çok, çok düşük seviyede değil. JQuery açısından sadece düşük seviyededir, çünkü $ .ajax ve tüm seçeneklerinden çok daha basit olan $ .get, $ .post, vb.
Till

16
Ah oğlum! Üzgünüm cevabınızı "kabul etmemeliydim", yine de çok yardımcı oldu. Şey, yönlendirmeler otomatik olarak XMLHttpRequest tarafından yönetilir, bu yüzden her zaman yeniden yönlendirme (iç!) Sonra 200 durum kodu almak. HTML'yi ayrıştırmak ve bir işaretçi aramak gibi kötü bir şey yapmak zorunda kalacağımı düşünüyorum.
Elliot Vargas

1
Sadece merak ediyorum, oturum sunucuda sona ererse, bu sunucu farklı bir SESSIONID gönderdiğini göstermez mi? sadece tespit edemedik mi?
Salamander2007

10
NOT: Bu yönlendirmeler için çalışmaz. ajax yeni sayfaya gider ve durum kodunu döndürür.

33

Sadece yaklaşımımı paylaşmak istedim çünkü bu birisine yardımcı olabilir:

Temelde kullanıcı adını görüntüleme gibi kimlik doğrulama şeylerini ve bu durumda giriş sayfasına yönlendirmeyi işleyen bir JavaScript modülü ekledim .

Benim senaryom: Temelde, aralarında tüm istekleri dinleyen ve giriş sayfamıza bir 302 ve konum başlığı ile yanıt veren bir ISA sunucumuz var .

JavaScript modülümde ilk yaklaşımım şöyle bir şeydi:

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

Sorun (burada daha önce de belirtildiği gibi), tarayıcının yönlendirmeyi kendisi tarafından yönetilmesidir, bu nedenle ajaxCompletegeri çağrım hiç çağrılmadı, bunun yerine açık bir şekilde zaten yönlendirilmiş olan Giriş sayfasının yanıtını aldım status 200. Sorun: Başarılı 200 yanıtının gerçek giriş sayfanız mı yoksa başka bir rasgele sayfa mı olduğunu nasıl tespit edersiniz?

Çözüm

302 yönlendirme yanıtı yakalayamadığım için, LoginPagegiriş sayfama giriş sayfasının URL'sini içeren bir başlık ekledim . Modülde şimdi başlığı dinliyorum ve bir yönlendirme yapıyorum:

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

... ve bu cazibe gibi çalışıyor :). URL'yi neden LoginPagebaşlığa dahil ettiğimi merak edebilirsiniz ... iyi temelde çünkü nesneden GETotomatik konum yönlendirmesinden kaynaklanan URL'yi belirlemenin bir yolunu bulamadım xhr...


+1 - ancak özel başlıkların başlaması gerekir X-, bu nedenle kullanmak için daha iyi bir başlık olur X-LoginPage: http://example.com/login.
uınbɐɥs

6
@ShaquinTrifonoff Artık değil. X-önekini kullanmadım çünkü Haziran 2011'de bir ITEF belgesi kullanımdan kaldırılmalarını önerdi ve gerçekten de Haziran 2012 ile özel başlıkların artık ön ekli olması resmi değil X-.
Juri

Ayrıca bir ISA sunucumuz var ve ben de aynı sorunla karşılaştım. Kod içinde çalışmak yerine, ISA'yı yeniden yönlendirmeyi durdurmak üzere yapılandırmak için kb2596444'teki yönergeleri kullandık .
scott stone

29

Bu konunun eski olduğunu biliyorum, ama burada bulduğum ve daha önce açıkladığım başka bir yaklaşım vereceğim . Temelde WIF ile ASP.MVC kullanıyorum (ama bu konunun bağlamı için bu gerçekten önemli değil - hangi çerçeveler kullanılırsa kullanılsın, cevap yeterli. ) .

Aşağıda gösterilen yaklaşım kutudan çıkan tüm ajax isteklerine uygulanabilir (eğer açık bir şekilde beforeSend olayını açıkça tanımlamazlarsa).

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

Herhangi bir ajax isteği gerçekleştirilmeden önce CheckPulseyöntem çağrılır (en basit herhangi bir şey olabilecek denetleyici yöntemi):

[Authorize]
public virtual void CheckPulse() {}

Kullanıcı kimliği doğrulanmazsa (jetonun süresi dolmuşsa) bu yönteme erişilemez ( Authorizeöznitelik tarafından korunur ). Çerçeve kimlik doğrulamasını işlediğinden, belirtecin süresi dolarken http durumunu 302 yanıta koyar. Tarayıcınızın 302 yanıtını şeffaf bir şekilde işlemesini istemiyorsanız, Global.asax'ta yakalayın ve yanıt durumunu değiştirin - örneğin 200 Tamam. Ayrıca, bu yanıtı özel bir şekilde (daha sonra istemci tarafında) işlemenizi isteyen üstbilgi ekleyin:

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

Son olarak, istemci tarafında bu özel başlığı kontrol edin. Varsa - oturum açma sayfasına tam yönlendirme yapılmalıdır (benim durumumda window.location, çerçevem ​​tarafından otomatik olarak işlenen istekten url ile değiştirilir).

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 olayı yerine PostAuthenticateRequest olayını kullanarak sorunu giderdim.
Frinavale

@JaroslawWaliszko Yanlış yanıtı son cevaba yapıştırdım! Ben PreSendRequestHeaders olay ..... demek PostAuthenticateRequest değil! >> allık << benim hatamı işaret ettiğiniz için teşekkür ederim.
Frinavale

WIF kullanırken @JaroslawWaliszko, AJAX istekleri için 401 yanıtı döndürebilir ve javascript'inizin bunları ele almasına izin verebilirsiniz. Ayrıca, tüm 302 kodlarının her durumda doğru olmayabilecek kimlik doğrulaması gerektirdiğini varsayıyorsunuz. Eğer ilgilenen varsa bir cevap ekledim.
Rob

26

Bu işlemek için daha iyi bir yol, özellikle mevcut HTTP protokol yanıt kodları kaldıraç olduğunu düşünüyorum 401 Unauthorized.

İşte nasıl çözdüm:

  1. Sunucu tarafı: Oturumun süresi dolarsa ve istek ajax ise. 401 yanıt kodu başlığı gönder
  2. Müşteri tarafı: ajax olaylarına bağlanma

    $('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 bu daha genel ve bazı yeni özel özellik / başlık yazmıyorsunuz. Ayrıca, mevcut ajax çağrılarınızdan herhangi birini değiştirmeniz gerekmez.

Düzenleme: @ Rob'un aşağıdaki yorumuna göre, 401 (kimlik doğrulama hataları için HTTP durum kodu) gösterge olmalıdır. Daha fazla ayrıntı için bkz. 403 Yasak vs 401 Yetkisiz HTTP yanıtları . Bununla birlikte, bazı web çerçeveleri hem kimlik doğrulama hem de yetkilendirme hataları için 403 kullanır - bu nedenle buna göre uyarlayın. Teşekkürler Rob.


Aynı yaklaşımı kullanıyorum. JQuery gerçekten 403 hata kodunda ajaxSuccess çağırıyor mu? Bence sadece ajaxError kısmı aslında gerekli
Marius Balčytis

24

Bu sorunu şu şekilde çözdüm:

Cevabı işlemek için bir ara katman yazılımı ekleyin, eğer bir ajax isteği için bir yönlendirme ise, yanıtı yönlendirme url'si ile normal bir yanıta değiştirin.

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

Daha sonra ajaxComplete öğesinde, yanıt yeniden yönlendirme içeriyorsa, bunun bir yönlendirme olması gerekir, bu nedenle tarayıcının konumunu değiştirin.

$('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

Benim için çalışan basit bir çözüm var, hiçbir sunucu kodu değişikliği gerekli ... sadece küçük hindistan cevizi bir çay kaşığı ekleyin ...

$(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 etiketinin varlığını kontrol ediyorum, ancak giriş sayfanızda hangi benzersiz dize varsa aramak için indexOf'u değiştirebilirsiniz ...


Bu benim için çalışmıyor gibi görünüyor, ajax çağrısı ile tanımlanan işlevi çağırmaya devam ediyor, başarı yöntemini geçersiz kılmıyor gibi.
adriaanp

En azından artık benim için çalışmıyor gibi görünüyor. Burada olası açıklama: stackoverflow.com/a/12010724/260665
Raj Pawan Gumdal

20

Bulduğum başka bir çözüm (özellikle küresel bir davranış ayarlamak istiyorsanız yararlıdır) $.ajaxsetup()yöntemi statusCodeözellik ile birlikte kullanmaktır . Diğerlerinin de işaret ettiği gibi, yönlendirme durum kodu ( 3xx) kullanmayın, bunun yerine bir durum 4xxkodu kullanın ve yönlendirme istemci tarafını kullanın.

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

400İşlemek istediğiniz durum koduyla değiştirin . Daha önce de belirtildiği gibi 401 Unauthorizediyi bir fikir olabilir. Çok özel 400olmadığı 401için kullanıyorum ve daha spesifik durumlar için (yanlış giriş bilgileri gibi) kullanabilirim. Bu nedenle 4xx, oturum zaman aşımına uğradığında ve yönlendirme istemci tarafını yönettiğinizde, doğrudan arka ucunuz yeniden yönlendirmek yerine bir hata kodu döndürmelidir . Backbone.js gibi çerçevelerde bile benim için mükemmel çalışıyor


Sayfadaki işlevden nerede bahsedilir?
Vikrant

@Vikrant Sorunuzu doğru anlarsam, jQuery yüklendikten hemen sonra ve gerçek isteklerinizi yapmadan önce işlevi çağırabilirsiniz.
morten.c

19

Verilen çözümlerin çoğu, fazladan bir üstbilgi veya uygun olmayan bir HTTP kodu kullanarak bir geçici çözüm kullanır. Bu çözümler büyük olasılıkla işe yarayacak, ancak biraz 'çılgın' hissedecek. Başka bir çözüm buldum.

401 yanıtında yönlendirmek için (passiveRedirectEnabled = "true") yapılandırılmış WIF kullanıyoruz. Yönlendirme, normal istekleri işlerken yararlıdır, ancak AJAX istekleri için çalışmaz (tarayıcılar 302 / yönlendirme işlemini yürütmediğinden).

Global.asax dosyasında aşağıdaki kodu kullanarak AJAX istekleri için yönlendirmeyi devre dışı bırakabilirsiniz:

    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;
        }
    }

Bu, JavaScript'in sayfayı yeniden yükleyerek işleyebileceği AJAX istekleri için 401 yanıt döndürmenizi sağlar. Sayfayı yeniden yüklemek, WIF tarafından işlenecek bir 401 atar (ve WIF, kullanıcıyı giriş sayfasına yönlendirir).

401 hatalarını işlemek için örnek bir javascript:

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

Güzel çözüm. Teşekkürler.
DanMan

19

Bu sorun daha sonra ASP.NET MVC RedirectToAction yöntemini kullanarak görünebilir. Formun div içinde yanıtı görüntülemesini önlemek için $ .ajaxSetup ile gelen yanıtlar için bir çeşit ajax yanıt filtresi yapabilirsiniz . Yanıt MVC yeniden yönlendirmesi içeriyorsa, JS tarafında bu ifadeyi değerlendirebilirsiniz. Aşağıdaki JS için örnek kod:

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

Veri ise: "window.location = '/ Acount / Login'" yukarıdaki filtre bunu yakalar ve verilerin görüntülenmesine izin vermek yerine yeniden yönlendirme yapmayı değerlendirir.


datayanıt gövdesinde veya üstbilgisinde mi?
GMsoF

16

Vladimir Prudnikov ve Thomas Hansen'in söylediklerini bir araya getirmek:

  • Bir XHR olup olmadığını tespit etmek için sunucu tarafı kodunuzu değiştirin. Öyleyse, yönlendirmenin yanıt kodunu 278 olarak ayarlayın. Django'da:
   if request.is_ajax():
      response.status_code = 278

Bu, tarayıcının yanıtı bir başarı olarak ele almasını ve Javascript'inize teslim etmesini sağlar.

  • JS'nizde, form gönderiminin Ajax üzerinden yapıldığından emin olun, yanıt kodunu kontrol edin ve gerekirse yeniden yönlendirin:
$('#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

Deneyin

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

Giriş sayfasına koy. Ana sayfada bir div'a yüklendiyse, giriş sayfasına yönlendirecektir. "#site", giriş sayfası dışındaki tüm sayfalarda bulunan bir div kimliğidir.


12

Spring Security kullanıyorsanız yanıtlar insanlar için çalışıyor gibi görünse de LoginUrlAuthenticationEntryPoint'i genişletiyor ve AJAX'ı daha sağlam işlemek için belirli bir kod ekledim. Örneklerin çoğu, yalnızca kimlik doğrulama hatalarını değil tüm yönlendirmeleri de engeller . Üzerinde çalıştığım proje için bu istenmeyen bir durumdu. Başarısız AJAX isteğinin önbelleğe alınmasını istemiyorsanız, ExceptionTranslationFilter öğesini genişletme ve önbelleğe alma adımını kaldırmak için "sendStartAuthentication" yöntemini geçersiz kılma gereksinimi bulabilirsiniz.

Örnek 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"));
    }
}

Kaynaklar: 1 , 2


3
Aşağı seçmenlerin neden aşağı oy kullandıklarını açıklaması yararlı olur (bana göre). Bu çözümde kötü bir şey varsa hatalarımdan öğrenmek istiyorum. Teşekkürler.
John

Spring kullanıyorsanız ve ayrıca JSF kullanıyorsanız, bunu da kontrol edin: ("kısmi / ajax"). EqualsIgnoreCase (request.getHeader ("faces-request"));
J Slick

Kullanıcılar, oy kullanmadığınız için oy vermiş olabilir: (1) hata yanıtınızı algılamak için gerekli istemci tarafı modları; (2) özelleştirilmiş LoginUrlAuthenticationEntryPoint'inizi filtre zincirine eklemek için Bahar yapılandırmasında gerekli modlar.
J Slick

Cevabınız buna @Arpad'den benzer. Benim için çalıştı; Spring Security kullanan 3.2.9. stackoverflow.com/a/8426947/4505142
Darren Parker

11

Bunu login.php sayfama aşağıdakileri koyarak çözdüm.

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

10

@Steg tarafından açıklanan sorunu tekrar alıntı yapmama izin verin

Sizinkine benzer bir sorunum vardı. 2 olası yanıtı olan bir ajax isteği gerçekleştiriyorum: biri tarayıcıyı yeni bir sayfaya yönlendiriyor ve biri mevcut sayfadaki mevcut bir HTML formunu yenisiyle değiştiriyor.

IMHO bu gerçek bir zorluk ve resmen mevcut HTTP standartlarına kadar genişletilmesi gerekecek.

Yeni Http Standardının yeni bir durum kodu kullanacağına inanıyorum. Anlamı: şu anda 301/302tarayıcıya bu isteğin içeriğini yenisine almasını söyler location.

Genişletilmiş standartta, yanıt status: 308(sadece bir örnek) ise, tarayıcının ana sayfayı locationsağlanan sayfaya yönlendirmesi gerektiğini söyleyecektir .

Söyleniyor ki; Zaten bu gelecekteki davranışı taklit eğilimindedir ve bu nedenle bir document.redirect gerektiğinde, ben sunucu olarak yanıt var:

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

JS " status: 204" aldığında, x-status: 308üstbilginin varlığını kontrol eder ve üstbilgide sağlanan sayfaya bir document.redirect yapar location.

Bu sizin için bir anlam ifade ediyor mu?


7

Bazıları aşağıdakileri yararlı bulabilir:

Yetkilendirme belirteci olmadan gönderilen herhangi bir işlem için istemcilerin giriş sayfasına yönlendirilmesini istedim. Tüm geri kalan eylemlerim Ajax tabanlı olduğundan, Ajax başarı işlevini kullanmak yerine giriş sayfasına yönlendirmek için iyi bir genel yola ihtiyacım vardı.

Ben de bunu yaptım:

Herhangi bir Ajax isteğinde sunucum Json 200 yanıtını "KİMLİK DOĞRULAMAYA İHTİYAÇ" döndürür (istemcinin kimliğinin doğrulanması gerekiyorsa).

Java'da basit örnek (sunucu tarafı):

@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'imde şu kodu ekledim:

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

Ve bununla ilgili.


5

sunucu uygulamasında response.setStatus(response.SC_MOVED_PERMANENTLY); , yeniden yönlendirme için ihtiyacınız olan '301' xmlHttp durumunu göndermeniz gerekir ...

ve $ .ajax fonksiyonunda fonksiyonu kullanmamalısınız .toString()..., sadece

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

sorun çok esnek olmaması, nereye yönlendirmek istediğinize karar veremezsiniz.

sunucu uygulamalarına yönlendirme en iyi yol olmalıdır. ama yine de bunu yapmanın doğru yolunu bulamıyorum.


5

Sadece tüm sayfa için herhangi bir ajax isteklerine kilitlemek istedim. @ SuperG beni başlattı. İşte sonuçta:

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

Kararımı dayandırmak için belirli http durum kodlarını özel olarak kontrol etmek istedim. Ancak, sadece yazmış olabileceğim başarıdan başka bir şey (sadece 200?) Almak için ajaxError'a bağlanabilirsiniz:

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

1
ikincisi sorun gidermeyi sorunlu hale getiren diğer hataları gizler
Tim Abell

1
403, kullanıcının kimliği doğrulanmadığı anlamına gelmez, (muhtemelen kimliği doğrulanmış) kullanıcının istenen kaynağı görüntüleme iznine sahip olmadığı anlamına gelir. Bu yüzden giriş sayfasına yönlendirmemelidir
Rob

5

Ayrıca değerleri iletmek istiyorsanız, oturum değişkenlerini ayarlayabilir ve bunlara erişebilirsiniz. Örn: jsp'nizde

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

Ve sonra bu geçici değeri javascript değişkeninizde saklayabilir ve oynayabilirsiniz


5

Üstbilgi çözümü ile herhangi bir başarı elde etmedim - onlar asla ajaxSuccess / ajaxComplete yönteminde alınmadı. Steg'in cevabını özel cevapla kullandım, ancak JS tarafını biraz değiştirdim. Standart $.getve $.postyöntemleri kullanabilmem için her işlevde çağırdığım bir yöntem ayarladım.

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

Kullanımdaki örneği ...

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

Son olarak, bir özel ekleyerek sorunu çözdüm HTTP Header. Sunucu tarafındaki her istek için yanıttan hemen önce, yanıtın başlığına istenen URL'yi ekliyorum.

Sunucudaki uygulama türüm Asp.Net MVCve bunu yapmak için iyi bir yeri var. içinde Global.asaxi hayata Application_EndRequestolayı böylece:

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

    }

Benim için mükemmel çalışıyor! Şimdi JQuery $.posti'nin her cevabında, duruma urlgöre POSTyöntemin bir sonucu olarak talep edilen ve ayrıca diğer yanıt başlıkları var 302,303 ....

ve diğer önemli şey, sunucu tarafında veya istemci tarafında kod değiştirmeye gerek olmamasıdır.

ve bir sonraki işlem, bu tür hatalar, mesajlar ve ... gibi eylem sonrası diğer bilgilere erişme yeteneğidir.

Bunu gönderdim, belki birine yardım et :)


4

Bu sorunu dinkgo uygulamasında uğraşıyordum (feragatname: Öğrenmek için uğraşıyorum ve hiçbir şekilde uzman değilim). Ne yapmak istedim jQuery ajax bir kaynağa bir DELETE isteği göndermek, sunucu tarafında silmek, sonra (temelde) ana sayfasına geri bir yönlendirme göndermek oldu. HttpResponseRedirect('/the-redirect/')Python komut dosyasından gönderdiğimde , jQuery'nin ajax yöntemi 302 yerine 200 alıyordu. Yani, yaptığım 300 ile bir yanıt göndermek oldu:

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

Sonra jQuery.ajax ile istemcide isteği gönderdim / işledim:

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

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

Belki 300 kullanmak "doğru" değildir, ama en azından istediğim gibi çalıştı.

Not: Bu, SO'nun mobil sürümünde düzenlemek için büyük bir acıydı. Aptal ISS, cevabımı bitirdiğimde hizmet iptali isteğimi hemen yerine getirdi!


4

XMLHttpRequest gönderme prototipini de bağlayabilirsiniz. Bu, tek bir işleyici ile tüm gönderiler (jQuery / dojo / etc) için çalışacaktır.

500 sayfanın süresi dolmuş bir hatayı işlemek için bu kodu yazdım, ancak 200 yönlendirmesini yakalamak için de işe yaramalı. XMLHttpRequest üzerindeki wikipedia girdisini readyState'in anlamı hakkında onreadystatechange üzerinde hazırlayın.

// 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

Ayrıca büyük olasılıkla kullanıcıyı üstbilgilerin URL'sinde verilen yönlendirmeye yönlendirmek isteyeceksiniz. Sonunda şöyle görünecek:

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

UPD: Karşıt. Aynı göreve sahip ama işe yaramıyor. Bu şeyleri yapıyor. Onu bulduğumda size çözüm göstereceğim.


4
Bu cevap yazarın kendisi işe yaramadığını söylediğinden silinmelidir. Daha sonra çalışan bir çözüm yayınlayabilir. -1
TheCrazyProgrammer

Fikir iyi, ama sorun "Konum" başlığının asla geçirilmemesidir.
Martin Zvarík

Düzenlemem reddedildi, bu nedenle ... "var xmlHttp = $ .ajax ({...." değişkeni içeride olamaz ...) kullanmanız gerekir: ;
Martin Zvarík

2

@John ve @Arpad bağlantısından ve @RobWinch bağlantısından yanıtları kullanarak çalışan bir çözüm aldım

Spring Security 3.2.9 ve jQuery 1.10.2 kullanıyorum.

Spring'in sınıfını yalnızca AJAX isteklerinden 4XX yanıtına neden olacak şekilde genişletin:

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'lerimde, burada gösterildiği gibi genel bir AJAX hata işleyicisi ekleyin

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

Ayrıca, mevcut hata işleyicilerini JSP sayfalarındaki AJAX çağrılarından kaldırın:

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

Umarım başkalarına yardımcı olur.

Güncelleme1 Form-login yapılandırmasına seçeneği (her zaman use-default-target = "true") eklemem gerektiğini buldum. Bir AJAX isteği giriş sayfasına (süresi dolmuş oturum nedeniyle) yönlendirildikten sonra, Spring önceki AJAX isteğini hatırlar ve giriş yaptıktan sonra otomatik olarak yönlendirir. Bu, döndürülen JSON'un tarayıcı sayfasında görüntülenmesine neden olur. Tabii ki, istediğim gibi değil.

Güncelleme2always-use-default-target="true" requstCache'den AJAX isteklerini engelleme @RobWinch örneğini kullanmak yerine . Bu, normal bağlantıların giriş yaptıktan sonra orijinal hedeflerine yönlendirilmesine izin verir, ancak AJAX giriş yaptıktan sonra ana sayfaya gider.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.