Obtuve una solución funcional usando las respuestas de @John y @Arpad link y @RobWinch link
Yo uso Spring Security 3.2.9 y jQuery 1.10.2.
Extienda la clase de Spring para causar una respuesta 4XX solo de solicitudes 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>
En mis JSP, agregue un controlador de errores AJAX global como se muestra aquí
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
Además, elimine los controladores de errores existentes de las llamadas AJAX en las páginas 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");
// }
});
Espero que ayude a los demás.
Actualización1
Descubrí que necesitaba agregar la opción (always-use-default-target = "true") a la configuración de inicio de sesión de formulario. Esto era necesario ya que después de que una solicitud AJAX se redirige a la página de inicio de sesión (debido a una sesión expirada), Spring recuerda la solicitud AJAX anterior y se redirige automáticamente después de iniciar sesión. Esto hace que el JSON devuelto se muestre en la página del navegador. Por supuesto, no es lo que quiero.
Actualización2
En lugar de usar always-use-default-target="true"
, use el ejemplo @RobWinch de bloquear solicitudes AJAX de requstCache. Esto permite que los enlaces normales se redirijan a su destino original después de iniciar sesión, pero AJAX va a la página de inicio después de iniciar sesión.