Tôi đã có một giải pháp làm việc bằng cách sử dụng các câu trả lời từ liên kết @John và @Arpad và liên kết @RobWinch
Tôi sử dụng Spring Security 3.2.9 và jQuery 1.10.2.
Mở rộng lớp của Spring để chỉ phản hồi 4XX từ các yêu cầu AJAX:
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>
Trong các tệp JSP của tôi, thêm trình xử lý lỗi AJAX toàn cầu như được hiển thị ở đây
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
Ngoài ra, xóa các trình xử lý lỗi hiện có khỏi các cuộc gọi AJAX trong các trang JSP:
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");
// }
});
Tôi hy vọng nó sẽ giúp người khác.
Update1
Tôi thấy rằng tôi cần thêm tùy chọn (luôn luôn sử dụng-default-target = "true") vào cấu hình đăng nhập mẫu. Điều này là cần thiết vì sau khi yêu cầu AJAX được chuyển hướng đến trang đăng nhập (do phiên hết hạn), Spring nhớ lại yêu cầu AJAX trước đó và tự động chuyển hướng đến nó sau khi đăng nhập. Điều này khiến JSON trả về được hiển thị trên trang trình duyệt. Tất nhiên, không phải những gì tôi muốn.
Update2
Thay vì sử dụng always-use-default-target="true"
, hãy sử dụng ví dụ @RobWinch về việc chặn các yêu cầu AJAX từ requstCache. Điều này cho phép các liên kết bình thường được chuyển hướng đến mục tiêu ban đầu của chúng sau khi đăng nhập, nhưng AJAX đi đến trang chủ sau khi đăng nhập.