私は$.post()
を使用してAjaxを使用してサーブレットを呼び出し、次に結果のHTMLフラグメントを使用してユーザーの現在のページのdiv
要素を置き換えます。ただし、セッションがタイムアウトすると、サーバーはユーザーをログインページに送信するためのリダイレクトディレクティブを送信します。この場合、jQueryはdiv
要素をログインページの内容に置き換えて、ユーザーの目にまれにまれなシーンを目撃させます。
JQuery 1.2.6で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を返すので、これは私たちにとってうまくいきました。また、ログインページにリダイレクトするだけで済みました。
私はこの質問を読み、ブラウザが透過的にリダイレクトを処理しないようにするために、レスポンスステータスコードを278に設定することに関して述べたアプローチを実行しました。これがうまくいったとしても、それはちょっとしたハックなので私は少し不満でした。
もっと掘り下げた後、私はこのアプローチを捨てて _ json _ を使いました。この場合、ajaxリクエストに対するすべてのレスポンスはステータスコード200を持ち、レスポンスの本文にはサーバー上に構築されたJSONオブジェクトが含まれています。その後、クライアント上のJavaScriptはJSONオブジェクトを使用して必要な処理を決定できます。
私はあなたと同じような問題を抱えていました。私は2つの可能な応答を持つajaxリクエストを実行します。1つはブラウザを新しいページにリダイレクトするもの、そして現在のページの既存の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"は、data.redirectとdata.formの2つのメンバーを持つようにサーバー上に構築されています。私はこのアプローチがはるかに優れていることがわかりました。
私はこの問題を解決しました:
レスポンスにカスタムヘッダを追加する:
public ActionResult Index(){
if (!HttpContext.User.Identity.IsAuthenticated)
{
HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
}
return View();
}
JavaScript関数を ajaxSuccess
イベントにバインドし、ヘッダーが存在するかどうかを確認します。
$(document).ajaxSuccess(function(event, request, settings) {
if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
window.location = '/';
}
});
どのブラウザも301と302の応答を正しく処理しません。そして実際、この規格では「透過的に」処理する必要があるとさえ言われています。これはAjax Libraryベンダーにとっては大きな頭痛の種です。 Ra-Ajax サーバーからの透過的なリダイレクトを処理するために、HTTPレスポンスステータスコード278(単に「未使用」の成功コード)の使用を余儀なくされた...
これは本当に私を悩ませます、そして、もし誰かがW3Cで何らかの "pull"を持っているならば、私はあなたがW3Cをさせることができることを認めるでしょう 知っています ;)
私はTimmerzの方法で、レモンを少しひねったものが好きです。 contentType of text/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();
}
});
低レベルの$.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';
}
これは誰かを助けるかもしれないので、私のアプローチを共有したかっただけです:
基本的には、ユーザー名の表示やログインページへのリダイレクトの処理などの認証機能を処理するJavaScriptモジュールを含めました。
私のシナリオ:基本的に、すべてのリクエストをリッスンするISAサーバーと、ログインページに2とロケーションヘッダーで応答するがあります。
私の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を含める理由を疑問に思うかもしれません...基本的に、GET
オブジェクトからの自動ロケーションリダイレクトから生じるxhr
のURLを特定する方法が見つからなかったためです...
このトピックは古いことは知っていますが、私が見つけて以前に説明した別のアプローチを ここ で紹介します。基本的に私はWIF でASP.MVCを使用しています(しかしこれはこのトピックの文脈ではあまり重要ではありません - どのフレームワークが使用されても答えは十分です。手掛かりは変わりません - 認証失敗に関する問題への対処ajaxリクエストを実行する) 。
以下に示すアプローチは、箱から出してすぐに使用できるすべてのajaxリクエストに適用できます(明らかにbeforeSendイベントを再定義しない場合)。
$.ajaxSetup({
beforeSend: checkPulse,
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.open();
document.write(XMLHttpRequest.responseText);
document.close();
}
});
Ajaxリクエストが実行される前に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;
}
}
});
}
これを処理するより良い方法は、既存のHTTPプロトコルレスポンスコード、特に401 Unauthorized
を利用することです。
これが私がどのように解決したかです:
クライアントサイド:ajaxイベントにバインドする
$('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禁止されたもの対401不正なHTTP応答 /詳細を見てください。そうは言っても、一部のWebフレームワークは認証と承認の両方のエラーに403を使用します。したがって、それに合わせて調整してください。ありがとうRob.
私はこのようにこの問題を解決しました:
レスポンスを処理するミドルウェアを追加します。それが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応答でリダイレクトするように構成されているWIFを使用しています(passiveRedirectEnabled = "true")。リダイレクトは通常のリクエストを処理するときには便利ですが、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がスローされ、WIFによって処理されます(そしてWIFはユーザーをログインページにリダイレクトします)。
401エラーを処理するための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);
}
});
私が見つけたもう1つの解決策(特にグローバルな振る舞いを設定したい場合に便利)は $.ajaxsetup()
メソッド と statusCode
プロパティ を一緒に使うことです。他の人が指摘したように、リダイレクトステータスコード(3xx
)を使用せず、代わりに4xx
ステータスコードを使用してリダイレクトクライアント側を処理します。
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
400
を処理したいステータスコードに置き換えます。すでに述べたように401 Unauthorized
は良い考えかもしれません。私は400
を使用していますが、それは非常に特殊なものではありません。また、401
をもっと特殊な場合に使用することもできます(間違ったログイン認証情報のように)。そのため、セッションがタイムアウトしたときにバックエンドが直接リダイレクトする代わりに4xx
エラーコードを返すようにして、クライアント側でリダイレクトを処理するようにします。 backbone.jsのようなフレームワークでも私にとっては完璧に動作します
この問題は、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'" 上記のフィルタはそれをキャッチし、データを表示させるのではなくリダイレクトするように評価します。
ウラジミール・プルドニコフとトーマス・ハンセンが言ったことをまとめる:
if request.is_ajax(): response.status_code = 278
これによりブラウザは応答を成功として扱い、あなたのJavascriptに渡します。
$('#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); });
私は私のために働く簡単な解決策を持っています、サーバーコードの変更は必要ありません...ちょうどナツメグの小さじを加えてください...
$(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を変更することができます...
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if(responseStatus=="success"){
window.location= "adminIndex.html";
}
});
}
</script>
やってみる
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
ログインページに載せてください。メインページのdivにロードされた場合は、ログインページにリダイレクトされます。 "#site"はログインページを除くすべてのページにあるdivのIDです。
Spring Securityを使用している場合、その答えは人々には効果があるように思われますが、LoginUrlAuthenticationEntryPointを拡張し、AJAXを処理するための特定のコードをより堅牢に追加しました。ほとんどの例では、 all を傍受して、認証の失敗だけではなくリダイレクトします。これは私が取り組んでいるプロジェクトには望ましくありませんでした。失敗した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"));
}
}
私はlogin.phpページに以下を入れることでこれを解決しました。
<script type="text/javascript">
if (top.location.href.indexOf('login.php') == -1) {
top.location.href = '/login.php';
}
</script>
以下が役に立つと思う人もいるでしょう。
認証トークンなしで送信される休止アクションのために、クライアントがログインページにリダイレクトされることを望みました。私の残りのアクションはすべて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);
}
};
});
そしてそれはそれについてです。
@Stegで説明されている問題をもう一度引用しましょう。
私はあなたと同じような問題を抱えていました。私は2つの可能な応答を持つajaxリクエストを実行します。ブラウザを新しいページにリダイレクトするものと現在のページの既存のHTMLフォームを新しいに置き換えるもの1。
私見これは本当の課題であり、正式に現在のHTTP標準に拡張する必要があります。
新しいHttp標準は新しいステータスコードを使うことになると思います。意味:現在301/302
はブラウザにthisrequestを新しいlocation
へ)の内容を取得するように伝えます。
拡張標準では、応答がstatus: 308
単なる例)の場合、ブラウザはメインページを提供されているlocation
にリダイレクトする必要があります。
言われていること。私はすでにこの_(futureのふるまいを模倣する傾向があるので、document.redirectが必要なときは、サーバーに次のように応答させます。
status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html
JSが "status: 204
"を取得すると、x-status: 308
ヘッダーの存在を確認し、location
ヘッダーで提供されているページへのdocument.redirectを行います。
これはあなたにとって意味がありますか?
サーブレットでは、リダイレクトに必要な '301' xmlHttpステータスを送信するために response.setStatus(response.SC_MOVED_PERMANENTLY);
と入力します。
$ .ajax関数では.toString()
関数を使用しないでください。
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
問題はそれがあまり柔軟ではないということです、あなたはあなたがリダイレクトしたい場所を決めることができません..
サーブレットを介してリダイレクトするのが最善の方法です。しかし、私はまだそれを行うための正しい方法を見つけることができません。
最後に、私はカスタム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
と、ステータス302
、303
、…によるPOST
メソッドの結果として得られるその他の応答ヘッダーがあります。
その他の重要な点は、サーバー側でもクライアント側でもコードを変更する必要がないということです。
そして次は、エラー、メッセージなどのアクション後の他の情報へのアクセスを取得する機能です。
私はこれを掲載した、多分誰かを助ける:)
私はヘッダソリューションに成功していませんでした - それらは私の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;
}
私はただページ全体のためのどんな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");
}
値も渡したい場合は、セッション変数を設定してにアクセスすることもできます。例: jspでは、次のように書くことができます。
<% HttpSession ses = request.getSession(true);
String temp=request.getAttribute("what_you_defined"); %>
そして、あなたはこの一時的な値をあなたのjavascript変数に格納して、そして試してみることができます
私は私がいじっているDjangoアプリでこの問題を抱えていました(免責事項:私は学びたいと思っています、そして決して専門家ではありません)。私がやりたかったのはjQuery ajaxを使ってDELETEリクエストをリソースに送り、それをサーバーサイドで削除し、そしてリダイレクトを(基本的に)ホームページに送り返すことでした。 Pythonスクリプトから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は私のサービス取消し要求を正しく通過させました!
XMLHttpRequest送信プロトタイプをフックすることもできます。これは1つのハンドラですべての送信(jQuery/dojo/etc)に対して機能します。
私は500ページの期限切れエラーを処理するためにこのコードを書きましたが、200リダイレクトをトラップするのにも同様にうまくいくはずです。 readyStateの意味についての XMLHttpRequest onreadystatechangeのウィキペディアのエントリを用意してください。
// 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);
}
さらにあなたはたぶんユーザーを与えられたヘッダURLにリダイレクトしたいでしょう。だから最後にそれはこのようになります:
$.ajax({
//.... other definition
complete:function(xmlHttp){
if(xmlHttp.status.toString()[0]=='3'){
top.location.href = xmlHttp.getResponseHeader('Location');
}
});
UPD:オップス。同じ仕事をしなさい、しかしそれは働かない。これをやる。解決方法がわかりましたら、お見せします。
@ Johnと@Arpad link と@RobWinch 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エラーハンドラを追加します。
$( 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 form-login configにオプション(always-use-default-target = "true")を追加する必要があることがわかりました。 AJAXリクエストが(期限切れのセッションのために)ログインページにリダイレクトされた後、Springは前回のAJAXリクエストを記憶し、ログイン後に自動的にリダイレクトします。これにより、返されたJSONがブラウザページに表示されます。もちろん、私が欲しいものではありません。
Update2 always-use-default-target="true"
を使用する代わりに、requstCacheからのAJAX要求をブロックする@RobWinchの例を使用してください。これにより、通常のリンクはログイン後に元のターゲットにリダイレクトされますが、AJAXはログイン後にホームページに移動します。