我使用 $.post()
通过 Ajax 调用 servlet,然后使用生成的 HTML 片段替换用户当前页面中的 div
元素。但是,如果会话超时,服务器会发送重定向指令将用户发送到登录页面。在这种情况下,jQuery 正在将 div
元素替换为登录页面的内容,从而迫使用户的眼睛确实见证了一个罕见的场景。
如何使用 jQuery 1.2.6 管理来自 Ajax 调用的重定向指令?
HttpContext.Response.AddHeader
并检查 ajaxsetup 成功是要走的路
我阅读了这个问题并实施了关于将响应 HTTP 状态代码设置为 278 的方法,以避免浏览器透明地处理重定向。即使这有效,我还是有点不满意,因为它有点像黑客。
经过更多的挖掘,我放弃了这种方法并使用了 JSON。在这种情况下,对 AJAX 请求的所有响应都具有 状态码 200,并且响应的主体包含在服务器上构造的 JSON 对象。然后客户端上的 JavaScript 可以使用 JSON 对象来决定它需要做什么。
我遇到了和你类似的问题。我执行了一个 AJAX 请求,它有 2 个可能的响应:一个将浏览器重定向到新页面,另一个将当前页面上的现有 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 对象“数据”在服务器上构造为具有 2 个成员:data.redirect
和 data.form
。我发现这种方法要好得多。
我通过以下方式解决了这个问题:
向响应中添加自定义标头: public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } 返回视图();将 JavaScript 函数绑定到 ajaxSuccess 事件并检查标头是否存在: $(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1' ) { window.location = '/'; } });
没有浏览器正确处理 301 和 302 响应。事实上,该标准甚至说他们应该“透明地”处理它们,这对于 Ajax 库供应商来说是一个非常头疼的问题。在 Ra-Ajax 中,我们被迫使用 HTTP 响应状态代码 278(只是一些“未使用”的成功代码)来透明地处理来自服务器的重定向...
这真的让我很恼火,如果这里有人对 W3C 有一些“吸引力”,我会很感激你可以让 W3C 知道我们确实需要自己处理 301 和 302 代码......! ;)
最终实现的解决方案是对 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,我们用它来替换页面的一部分。此外,我们只需要重定向到登录页面。
我喜欢蒂默兹的方法,稍微加一点柠檬。如果您在期待 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();
}
});
使用低级 $.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 服务器,在它之间侦听所有请求,并以 302 和位置标头响应我们的登录页面。
在我的 JavaScript 模块中,我最初的方法类似于
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
问题(正如这里已经提到的许多问题)是浏览器自己处理重定向,因此我的 ajaxComplete
回调从未被调用,而是我得到了 已经重定向的登录页面的响应,这显然是a2}。问题:您如何检测成功的 200 响应是您的实际登录页面还是其他任意页面?
解决方案
由于我无法捕获 302 重定向响应,因此我在我的登录页面上添加了一个 LoginPage
标头,其中包含登录页面本身的 url。在模块中,我现在监听标头并进行重定向:
if(xhr.status === 200){
var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
window.location.replace(loginPageRedirectHeader);
}
}
...这就像魅力:)。您可能想知道为什么我将 url 包含在 LoginPage
标头中...基本上是因为我发现无法确定 GET
的 url,这是由于来自 xhr
对象的自动位置重定向...
我知道这个话题已经过时了,但我将提供另一种我发现并在之前描述过的方法 here。基本上我将 ASP.MVC 与 WIF 一起使用(但这对于本主题的上下文并不重要 - 无论使用哪种框架,答案都足够了。线索保持不变 - 处理与身份验证失败相关的问题,同时执行 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。发送 401 响应代码头 客户端:绑定到 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 Forbidden vs 401 Unauthorized HTTP responses。话虽如此,一些 Web 框架使用 403 来进行身份验证和授权错误 - 所以相应地进行调整。谢谢罗布。
我这样解决了这个问题:
添加中间件来处理响应,如果是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中,如果响应中包含redirect,那肯定是redirect,所以改变浏览器的位置。
$('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;
}
}
}
我发现的另一个解决方案(如果您想设置全局行为特别有用)是将 $.ajaxsetup()
method 与 statusCode
property 一起使用。就像其他人指出的那样,不要使用重定向状态码 (3xx
),而是使用 4xx
状态码并处理重定向客户端。
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
将 400
替换为您要处理的状态码。就像已经提到的 401 Unauthorized
可能是一个好主意。我使用 400
因为它非常不具体,我可以将 401
用于更具体的情况(例如错误的登录凭据)。因此,当会话超时并且您处理重定向客户端时,您的后端应该返回一个 4xx
错误代码,而不是直接重定向。即使使用像backbone.js这样的框架也非常适合我
我有一个适合我的简单解决方案,无需更改服务器代码……只需添加一茶匙肉豆蔻……
$(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 以搜索登录页面中存在的任何唯一字符串...
大多数给定的解决方案都使用了一种变通方法,即使用额外的标头或不合适的 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,这将由 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);
}
});
然后使用 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
在响应正文或标头中?
将 Vladimir Prudnikov 和 Thomas Hansen 所说的话放在一起:
更改您的服务器端代码以检测它是否是 XHR。如果是,则将重定向的响应代码设置为 278。在 django 中:
如果 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 { ... 你的代码在这里 ... } },数据:$(this)。序列化(),};$.ajax(选项);});
<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。大多数示例拦截所有重定向,而不仅仅是身份验证失败。这对我从事的项目来说是不可取的。如果您不希望缓存失败的 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"));
}
}
让我再次引用@Steg 描述的问题
我遇到了和你类似的问题。我执行了一个 ajax 请求,它有 2 个可能的响应:一个将浏览器重定向到新页面,另一个将当前页面上的现有 HTML 表单替换为新的表单。
恕我直言,这是一个真正的挑战,必须正式扩展到当前的 HTTP 标准。
我相信新的 Http 标准将使用新的状态码。含义:当前 301/302
告诉浏览器去获取 this 请求的内容到一个新的 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
标头中提供的页面。
这对你有意义吗?
我通过将以下内容放在我的 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);
}
};
});
就是这样。
在 servlet 中,您应该放置 response.setStatus(response.SC_MOVED_PERMANENTLY);
以发送重定向所需的“301”xmlHttp 状态...
在 $.ajax 函数中你不应该使用 .toString()
函数......,只是
if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }
问题是它不是很灵活,您无法决定要重定向的位置..
通过 servlet 重定向应该是最好的方法。但我仍然找不到正确的方法。
我只是想锁定整个页面的任何 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 变量中并玩转
我的标头解决方案没有任何成功——它们从未在我的 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;
}
最后,我通过添加自定义 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
以及其他响应标头,这些标头是 POST
方法的结果,状态为 302
,303
,...。
另一个重要的事情是无需在服务器端或客户端修改代码。
接下来是能够以这种方式访问发布操作的其他信息,例如错误、消息和……。
我发布了这个,也许可以帮助某人:)
我在我正在修补的 django 应用程序上遇到了这个问题(免责声明:我正在修补学习,绝不是专家)。我想做的是使用 jQuery ajax 向资源发送 DELETE 请求,在服务器端将其删除,然后将重定向发送回(基本上)主页。当我从 python 脚本发送 HttpResponseRedirect('/the-redirect/')
时,jQuery 的 ajax 方法收到的是 200 而不是 302。所以,我所做的是发送 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 并不“正确”,但至少它就像我想要的那样工作。
PS:在 SO 的移动版本上编辑这是一个巨大的痛苦。当我完成我的回答时,愚蠢的 ISP 将我的服务取消请求通过了!
您还可以挂钩 XMLHttpRequest 发送原型。这将适用于使用一个处理程序的所有发送(jQuery/dojo/etc)。
我编写了这段代码来处理 500 页过期错误,但它应该同样可以捕获 200 重定向。准备好 wikipedia entry on XMLHttpRequest onreadystatechange 关于readyState 的含义。
// 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);
}
使用@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 中,添加一个全局 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"
,而是使用 @RobWinch 阻止来自 requstCache 的 AJAX 请求的示例。这允许在登录后将普通链接重定向到其原始目标,但 AJAX 会在登录后转到主页。