Where to put custom post-authentication code using UsernamePasswordAuthenticationFilter - spring

I'm using Spring and custom implementation of UsernamePasswordAuthenticationFilter. I want to perform some custom code after successful authentication (for example: log a message with username that just got authenticated).
Which method should I override or how to register a handler for successful authentication ?
Is it good idea to override successfulAuthentication() method, put there my custom code and finish it with call to original method (super.successfulAuthentication();) ? Or there is some other best practise?

My approach for performing custom tasks after a successful
authentication is to use a Custom Authentication Success Handler in
Spring Security.
You can achieve this as below:
Create your custom AuthenticationSuccessHandler like TMGAuthenticationSuccessHandler. I have created a sample code which redirects user to the password change page, if the user is detected to be using the default machine generated password.
#Component("tMGAuthSuccessHandler")
public class TMGAuthSuccessHandler implements AuthenticationSuccessHandler {
private AuthenticationSuccessHandler target = new SavedRequestAwareAuthenticationSuccessHandler();
#Autowired
private UserService userService;
private static final Logger LOGGER = LoggerFactory.getLogger(TMGAuthSuccessHandler.class);
#Override
public void onAuthenticationSuccess(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Authentication authentication)
throws IOException, ServletException {
if (hasDefaultPassword(authentication)) {
LOGGER.debug("Default password detected for username: " + authentication.getName());
servletResponse.sendRedirect("changePassword");
} else {
target.onAuthenticationSuccess(servletRequest, servletResponse, authentication);
}
}
/**
* Checks whether default password is used in login.
*/
private boolean hasDefaultPassword(Authentication authentication) {
String username = authentication.getName();
User user = userService.findOnUsername(username, true, false, false, false);
if (user != null && user.getLoginAuditTrail() != null && user.getLoginAuditTrail().isDefaultPasswordUsed() != null) {
return user.getLoginAuditTrail().isDefaultPasswordUsed();
}
return false;
}
/**
* Proceeds to the requested URL.
*/
public void proceed(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Authentication authentication) throws IOException,
ServletException {
target.onAuthenticationSuccess(servletRequest, servletResponse, authentication);
}
}
Modify the securityContext.xml or similar file that contains spring security related configurations. Add this customHander to http configuration as authentication-success-handler-ref="tMGAuthSuccessHandler". Code snippet is shown below:
<security:http use-expressions="true" authentication-manager-ref="webAppAuthManager">
<!-- signin and signout -->
<security:intercept-url pattern="/signin" access="permitAll" />
<security:intercept-url pattern="/logout" access="permitAll" />
<security:intercept-url pattern="/accessDenied" access="permitAll"/>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<!-- sign in Configuration -->
<security:form-login login-page="/signin"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/signin?authFail=true"
authentication-success-handler-ref="inoticeAuthSuccessHandler" />
<security:logout logout-url="/signout" invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/signin?logout=true" />
</security:http>
You are good to go now.
Reference credit: How to use custom filter with authentication-success-handler-ref equivalent in spring security

You have at least two options:
AuthenticationSuccessHandler
ApplicationListener<E extends ApplicationEvent> with AbstractAuthenticationEvent

Related

How does spring security work with client certificate authentication when using a POST method

When I use RestTemplate of spring framework to connect to my web service which is secured by HTTPS with client authentication by spring security through Restful API, I found problem to use POST method. It seems that the X509AuthenticationFilter don't get the client certificate when I use POST method. I don't have the same problem when I use GET method.
The following is the XML configuration file for spring security in the server side.
<http pattern="/resources/**" security="none" />
<http auto-config="true" use-expressions="true" entry-point-ref="forbiddenAuthEntryPoint">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/service/**" access="hasRole('ROLE_ABC_USER')" />
<intercept-url pattern="/**" access="permitAll" />
<!-- <x509 subject-principal-regex="CN=(.*?)," user-service-ref="userDetailsService"
/> -->
<custom-filter position="X509_FILTER" ref="myX509AuthenticationFilter" />
</http>
<bean:bean id="myX509AuthenticationFilter"
class="com.ray.MyX509AuthenticationFilter">
<bean:property name="authenticationManager" ref="authenticationManager" />
</bean:bean>
<bean:bean id="preauthAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<bean:property name="preAuthenticatedUserDetailsService" ref="authenticationUserDetailsService" />
</bean:bean>
<bean:bean id="authenticationUserDetailsService"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<bean:property name="userDetailsService" ref="userDetailsService" />
</bean:bean>
<bean:bean id="forbiddenAuthEntryPoint"
class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthenticationProvider" />
<authentication-provider>
<user-service id="userDetailsService">
<user name="www.ray.insight" password="dummy" authorities="ROLE_ABC_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Originally, I use the standard x509 element in the security namespace as you can see from the comment out line. During my testing, I use the following MyX509AuthenticationFilter for debugging purpose.
public class MyX509AuthenticationFilter extends X509AuthenticationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(MyX509AuthenticationFilter.class);
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null && certs.length > 0) {
LOGGER.debug("X.509 client authentication certificate:" + certs[0]);
} else {
LOGGER.debug("No client certificate found in request.");
}
return super.getPreAuthenticatedPrincipal(request);
}
}
In order to further debug the request. I have added the following servlet filter in web.xml before spring security to print out the client certificate.
public class MyRequestFilter implements Filter {
Logger LOGGER = LoggerFactory.getLogger(MyRequestFilter.class);
#Override
public void doFilter(ServletRequest _servletRequest, ServletResponse _servletResponse, FilterChain _filterChain)
throws IOException, ServletException {
LOGGER.debug("MyRequestFilter doFilter(): Entering");
if (_servletRequest instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) _servletRequest;
String requestURI = httpServletRequest.getRequestURI();
String queryString = httpServletRequest.getQueryString();
StringBuffer requestURL = httpServletRequest.getRequestURL();
LOGGER.debug("requestURI ->" + requestURI + "<-");
LOGGER.debug("queryString ->" + queryString + "<-");
LOGGER.debug("requestURL ->" + requestURL.toString() + "<-");
X509Certificate[] certs = (X509Certificate[]) httpServletRequest
.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null && certs.length > 0) {
LOGGER.debug("X.509 client authentication certificate:" + certs[0]);
} else {
LOGGER.debug("No client certificate found in request.");
}
} else {
LOGGER.debug("get non HttpServletRequest!!" + _servletRequest);
}
_filterChain.doFilter(_servletRequest, _servletResponse);
}
}
In order to use client authentication, I have also setup the tomcat to use client authentication and the following line is added to the server.xml.
<Connector SSLEnabled="true" acceptCount="100" clientAuth="true"
disableUploadTimeout="true" enableLookups="false" keyAlias="abcServer"
keypass="password" keystoreFile="tomcat8Cert2.jks" keystorePass="password"
maxHttpHeaderSize="8192" maxSpareThreads="75" maxThreads="150"
minSpareThreads="25" port="443" scheme="https" secure="true"
sslProtocol="TLS" truststoreFile="trustStoreCert2.jks"
truststorePass="password" />
By using the above program and configuration, if I involve web service through GET method, everything work fine. However, if I involve web service through POST method, the MyRequestFilter can print out the certificate with the correct CN, but the MyX509AuthenticationFilter has not printed out anything and spring security just return "org.springframework.web.client.HttpClientErrorException: 403 Forbidden". This exception should come from Http403ForbiddenEntryPoint of spring security.
I have further test the same web service after comment out spring security filter in my web.xml of the server side and involve it through the same client code. It work. Hence, I suspect something wrong with my setting that make the spring security cannot work property with the POST method to do the client certificate authentication. Any body has idea what I have missed in spring security?
=====================
In case you may interesting to know my client code. The following is the code snippet in the client side.
I use the following to initialize the RestTemplate.
private void initialise() {
restTemplate = new RestTemplate();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient());
factory.setReadTimeout(30000);
factory.setConnectTimeout(30000);
restTemplate.setRequestFactory(factory);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
}
The following code is used to return the httpclient with all the required keystore.
KeyStore trustStore = KeyStore.getInstance(this.getKeyStoreType());
FileInputStream instream = new FileInputStream(new File(this.getKeyStorePath()));
try {
trustStore.load(instream, this.getKeyStorePassword().toCharArray());
} finally {
instream.close();
}
// TODO: Should trust only authorized client
TrustStrategy allTrust = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(
trustStore, allTrust)
.loadKeyMaterial(trustStore, this.getKeyStorePassword()
.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
Then the following code is used to involve the web service through POST.
response = restTemplate.postForEntity(this.getBasePath() + path, httpEntity, String.class);
And the following code is used to involve the web service through GET.
response = restTemplate.exchange(this.getBasePath() + path, HttpMethod.GET, httpEntity,
String.class);

Session timeout leads to Access Denied in Spring MVC when CSRF integration with Spring Security

I have Integrated CSRF token with Spring Security in my Spring MVC Project. Everything work properly with CSRF token, token will be send from client side to server side.
I have changed my logout process to make it POST method to send CSRF token and its works fine.
I have face problem when session timeout is occurred, it needs to be redirected to spring default logout URL but it gives me Access Denied on that URL.
How to override this behavior.
I have include below line in Security config file
<http>
//Other config parameters
<csrf/>
</http>
Please let me know if anyone needs more information.
The question is a bit old, but answers are always useful.
First, this is a known issue with session-backed CSRF tokens, as described in the docs: CSRF Caveats - Timeouts.
To solve it, use some Javascript to detect imminent timeouts, use a session-independent CSRF token repository or create a custom AccessDeniedHandler route. I chose the latter:
Config XML:
<http>
<!-- ... -->
<access-denied-handler ref="myAccessDeniedHandler"/>
</http>
<bean id="myAccessDeniedHandler" class="package.MyAccessDeniedHandler">
<!-- <constructor-arg ref="myInvalidSessionStrategy" /> -->
</bean>
MyAccessDeniedHandler:
public class MyAccessDeniedHandler implements AccessDeniedHandler {
/* ... */
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception)
throws IOException, ServletException {
if (exception instanceof MissingCsrfTokenException) {
/* Handle as a session timeout (redirect, etc).
Even better if you inject the InvalidSessionStrategy
used by your SessionManagementFilter, like this:
invalidSessionStrategy.onInvalidSessionDetected(request, response);
*/
} else {
/* Redirect to a error page, send HTTP 403, etc. */
}
}
}
Alternatively, you can define the custom handler as a DelegatingAccessDeniedHandler:
<bean id="myAccessDeniedHandler" class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">
<constructor-arg name="handlers">
<map>
<entry key="org.springframework.security.web.csrf.MissingCsrfTokenException">
<bean class="org.springframework.security.web.session.InvalidSessionAccessDeniedHandler">
<constructor-arg name="invalidSessionStrategy" ref="myInvalidSessionStrategy" />
</bean>
</entry>
</map>
</constructor-arg>
<constructor-arg name="defaultHandler">
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/my_error_page"/>
</bean>
</constructor-arg>
</bean>
The answer provided by mdrg is right on, and I also implemented a custom AccessDeniedHandler which I submit for your consideration:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
/**
* Intended to fix the CSRF Timeout Caveat
* (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-timeouts).
* When the session expires and a request requiring CSRF is received (POST), the
* missing token exception is handled by caching the current request and
* redirecting the user to the login page after which their original request will
* complete. The intended result is that no loss of data due to the timeout will
* occur.
*/
public class MissingCsrfTokenAccessDeniedHandler extends AccessDeniedHandlerImpl {
private RequestCache requestCache = new HttpSessionRequestCache();
private String loginPage = "/login";
#Override
public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException exception) throws IOException, ServletException {
if (exception instanceof MissingCsrfTokenException && isSessionInvalid(req)) {
requestCache.saveRequest(req, res);
res.sendRedirect(req.getContextPath() + loginPage);
}
super.handle(req, res, exception);
}
private boolean isSessionInvalid(HttpServletRequest req) {
try {
HttpSession session = req.getSession(false);
return session == null || !req.isRequestedSessionIdValid();
}
catch (IllegalStateException ex) {
return true;
}
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}
Wired up via java config:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
...
}
public AccessDeniedHandler getAccessDeniedHandler() {
return new MissingCsrfTokenAccessDeniedHandler();
}
}

Spring Security SWF : How to redirect to different flows based on some condition

I am using spring security along with spring web flow. The problem is that I need to redirect user to two different pages based on some condition during logging.
If the user is a first time logging user he will be redirected to firstTimeuser.jsp otherwise he will be redirected to homepage.jsp.
In db side i have a field IS_FIRST_TIME_USER which will be true for first time users.
so in my LOGIN table I have id,username,password,IS_FIRST_TIME_USER fields.
In spring-security.xml I have
<http auto-config="true">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login authentication-success-handler-ref="authenticationSuccessHandler"
login-page="/basic"
default-target-url="/basic1"
authentication-failure-url="/basic?error=true"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/basic?logout" />
</http>
Yes it is possible by providing a custom implementation of AuthenticationSuccessHandler using attribute authentication-success-handler-ref.
For example see here
Note: When using this pattern, you shouldn't use default-target-url
Simple Implementation in your case would be something like below
#Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if(isFirstTimeLogin(authentication.getName())) {
response.sendRedirect("/firstTimeuser");
} else {
response.sendRedirect("/homepage");
}
}
private boolean isFirstTimeLogin(String username) {
//code to access your DAO and figure out whether this is first time login or not
//Code to access your DAO and update the flag in database
return true;
}
}

Integrating Token based security into existing Spring Security web application

I am designing a RESTful web services that needs to be accessed by user after proper authentication. I have already developed Security for my application using Spring Security 3.0. Now I want to integrate TokenBasedAuthentication. But I stuck here for how do i do this.
My ApplicationContextSecurity.xml:
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<beans:bean id="myAccessDecisionManager"
class="com.app.security.MyAccessDecisionManager">
</beans:bean>
<http auto-config="true" once-per-request="true"
access-decision-manager-ref="myAccessDecisionManager"
access-denied-page="/jsp/errorPage.jsp">
<intercept-url pattern="/*.app" access="ROLE_ANONYMOUS" />
<form-login login-page="/login.app"
login-processing-url="/j_spring_security_check" default-target-url="/login/checking.app"
authentication-failure-url="/login.app?login_error=1" />
<logout logout-url="/j_spring_security_logout"
logout-success-url="/login.app" invalidate-session="true" />
<session-management invalid-session-url="/login.app"
session-fixation-protection="newSession">
<concurrency-control max-sessions="100"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider"></authentication-provider>
</authentication-manager>
<beans:bean id="customAuthenticationProvider"
class="com.app.security.CustomAuthenticationProvider">
</beans:bean>
My CustomAuthenticationProvider :
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private ILoginService loginService;
protected final transient Log log = LogFactory.getLog(getClass());
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
UsernamePasswordAuthenticationToken usernamePassswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(), authentication.getCredentials());
// Doing authentication process here and returning authentication token
return usernamePassswordAuthenticationToken;
}
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
My requirement is,
When user want to access rest web service at first time he should provide userName/password to the server from header.
Server will accept the request, check the authentication and generate token for future requests for specific period.
Also I need client side code for how to access secured web services.
Thanks.
When user want to access rest web service at first time he should
provide userName/password to the server from header.
Server will accept the request, check the authentication and generate
token for future requests for specific period
You can do this either using HTTP headers or a normal HTTP POST request mapped to a Spring MVC controller (this is how we do it in our apps):
#Controller
public class AuthenticationController {
#Autowired
#Qualifier("authenticationManager")
AuthenticationManager authenticationManager;
#Autowired
SecurityContextRepository securityContextRepository;
#RequestMapping(method = RequestMethod.POST, value = "/authenticate")
public #ResponseBody String authenticate(#RequestParam final String username, #RequestParam final String password, final HttpServletRequest request, final HttpServletResponse response) {
final UsernamePasswordAuthenticationToken authenticationRequest = new UsernamePasswordAuthenticationToken(username, password);
final Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
final String token = <some randomly generated secure token>;
final Authentication authentication = new MyAuthenticationToken(authenticationResult, token);
SecurityContextHolder.getContext().setAuthentication(authentication);
this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
return token;
}
}
Once this is done, the client should send the token in an HTTP header with every subsequent request.
Also I need client side code for how to access secured web services
Not sure what exactly you are looking for here. If your client is a JavaScript library running in a web browser, setting the authentication token as an HTTP header with every request should be straightforward. If your client is a device, the device could store the token in memory and include it as an HTTP header with every request using whatever HTTP client library you are using to invoke the services.

Detect Session Timeout in Ajax Request in Spring MVC

I can't see seem to find a good example/answer on how to send back some data from an ajax request when a session has timed out. It sends back the login page HTML and I want to either send json or a status code I can intercept.
The simplest way for doing this is using a filter on URLs of your AJAX requests.
In the example below I'm just sending HTTP 500 response code with a response body indicating the session timeout, but you can easily set the response code and body to what is more suitable for your case..
package com.myapp.security.authentication;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ExpiredSessionFilter extends GenericFilterBean {
static final String FILTER_APPLIED = "__spring_security_expired_session_filter_applied";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "SESSION_TIMED_OUT");
return;
}
chain.doFilter(request, response);
}
}
Here's an approach that I think is quite simple. It's a combination of approaches that I've observed on this site. I wrote a blog post about it:
http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/
The basic idea is to use an api url prefix (i.e. /api/secured) as suggested above along with an authentication entry point. It's simple and works.
Here's the authentication entry point:
package com.yoyar.yaya.config;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class AjaxAwareAuthenticationEntryPoint
extends LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
super(loginUrl);
}
#Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
boolean isAjax
= request.getRequestURI().startsWith("/api/secured");
if (isAjax) {
response.sendError(403, "Forbidden");
} else {
super.commence(request, response, authException);
}
}
}
And here's what goes in your spring context xml:
<bean id="authenticationEntryPoint"
class="com.yoyar.yaya.config.AjaxAwareAuthenticationEntryPoint">
<constructor-arg name="loginUrl" value="/login"/>
</bean>
<security:http auto-config="true"
use-expressions="true"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/api/secured/**" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/logout" access="permitAll"/>
<security:intercept-url pattern="/denied" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/" access="permitAll"/>
<security:form-login login-page="/login"
authentication-failure-url="/loginfailed"
default-target-url="/login/success"/>
<security:access-denied-handler error-page="/denied"/>
<security:logout invalidate-session="true"
logout-success-url="/logout/success"
logout-url="/logout"/>
</security:http>
I use the same solution by #Matt in backend. If you're using angularJs on front end, add below interceptor in angular $http to let browser actually do a redirect to login page.
var HttpInterceptorModule = angular.module('httpInterceptor', [])
.config(function ($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
})
.factory('myInterceptor', function ($q) {
return {
'responseError': function(rejection) {
// do something on error
if(rejection.status == 403 || rejection.status == 401) window.location = "login";
return $q.reject(rejection);
}
};
});
Note that below line is needed only if you're using AngularJs after version 1.1.1 (angularJS removed header "X-Requested-With" from that version onward)
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
Seeing as all of the present answers are a few years old now, I'll share my solution which I currently have working in a Spring Boot REST application:
#Configuration
#EnableWebSecurity
public class UISecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling.authenticationEntryPoint(authenticationEntryPoint());
...
}
private AuthenticationEntryPoint authenticationEntryPoint() {
// As a REST service there is no 'authentication entry point' like MVC which can redirect to a login page
// Instead just reply with 401 - Unauthorized
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
The basic premise here is that I override the authentication entry point which by default was issuing a redirect to my non-existent login page. It now responds by sending a 401. Spring also implicitly creates an standard error response JSON object that it returns as well.

Resources