Handling JWT Exception in Spring MVC - java

I am trying to implement Token Authentication on our REST Api, and currently I am referring to this article. On the article it discusses that on creating the token JWT was used, but my current problem is that every time an invalid token was being passed on to my application an exception is being created which is the JwtException.class and I want to catch that exception using my global exception handler class. I tried also to wrapped the JwtException on my application's exception class but to no avail the exception was not caught.
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(value={JwtException.class})
public ResponseEntity<?> handleTokenException(JwtException e){
return new ResponseEntity<Object>(HttpStatus.UNAUTHORIZED);
}
#ExceptionHandler(value={InvalidAuthTokenException.class})
public ResponseEntity<?> handleTokenException(InvalidAuthTokenException e){
return new ResponseEntity<Object>(HttpStatus.UNAUTHORIZED);
}
}

Your GlobalExceptionHandler isn't truly global, it will only catch exceptions that occur in your controller (hence ControllerAdvice), the exceptions you are running into are occurring in servlet filters, which is where Spring Security does pretty much all of its work. This little chart may help explain what I am talking about:
PreFilters <- Executed before entering your controller, decryption of JWT is happening here
Controller <- ControllerAdvice will catch all exceptions thrown here
PostFilters <- Executed after exiting your controller
Luckily Spring Security already has mechanisms in place for handling exceptions that occur when doing things like decrypting a JWT in a filter. You will want to update your SpringSecurityConfig like so. Note its important that the ExceptionTranslationFilter is after your StatelessAuthenticationFilter (or whatever you named the filter where the JWT decryption is occurring).
#Configuration
#EnableWebSecurity
#Order(2)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
ExceptionTranslationFilter = new ExceptionTranslationFilter(new AuthenticationExceptionHandler());
http.addFilterAfter(new StatelessAuthenticationFilter(tokenAuthenticationService),
ExceptionTranslationFilter.class);
}
}
public class AuthenticationExceptionHandler implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse, AuthenticationException e) throws IOException, ServletException {
//Logic on how to handle JWT exception goes here
}
}
public class StatelessAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
//DECRYPT YOUR JWT
} catch (Exception e) {
throw new AuthenticationException();//If you get an exception wrap it in a AuthenticationException (or a class that extends it)
}
}
}

Related

Filter or handle AuthenticationException in OAuth2

I am trying to filter the AuthenticationException that is thrown during a user Authentication in my application. I know these cannot be filtered with #ControllerAdvice and #ExceptionHandler. So trying to figure out any Handler would work for my problem.
Already tried different approaches like AuthenticationFailureHandler but they didn't fit my requirement as I am using ResourceServerConfigurerAdapter.
Please suggest.
Spring security exceptions are handled by ExceptionTranslationFilter. You can create a custom filter that handles AuthenticationException and add it after ExceptionTranslationFilter. Default Spring security Filter Ordering.
public class AuthenticationExceptionFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (final Exception exception) {
if (exception instanceof AuthenticationException) {
this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
}
if(exception instanceof AccessDeniedException) {
....
}
// Check ExceptionTranslationFilter#handleSpringSecurityException(...)
}
You can register the filter programmatically by overriding the configure method of WebSecurityConfigurerAdapter.
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new AuthenticationExceptionFilter(), ExceptionTranslationFilter.class);
}
For Centralized exception handling across all #RequestMapping:
Check out ResponseEntityExceptionHandler
A convenient base class for #ControllerAdvice classes that wish to
provide centralized exception handling across all #RequestMapping
methods through #ExceptionHandler methods.
This base class provides an #ExceptionHandler method for handling
internal Spring MVC exceptions.
Here's a sample code snippet to get you started:
#ControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler {
....
#ExceptionHandler({Exception.class})
public ResponseEntity<Object> handleCustomException(final CustomException exception, final WebRequest request) {
return handleExceptionInternal(exception,
ErrorOutputDto.create(exception.getErrorIdentifier(), exception.getMessage()),
new HttpHeaders(),
HttpStatus.UNAUTHORIZED,
request);
}
....

Catching exception thrown in AuthenticationProvider

I am implementing custom 'AuthenticationProvider'. If not authenticated I am throwing exception inside 'authenticate' function as shown below.
public class DelegatingLdapAuthenticationProvider implements AuthenticationProvider {
private ActiveDirectoryLdapAuthenticationProvider primaryProvider;
private List<ActiveDirectoryLdapAuthenticationProvider> secondaryProviders = new ArrayList<>();
public DelegatingLdapAuthenticationProvider() {
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Authentication result = null;
AuthenticationException exception = null;
try {
result = primaryProvider.authenticate(authentication);
} catch (AuthenticationException e) {
exception = e;
for (ActiveDirectoryLdapAuthenticationProvider secondaryProvider : secondaryProviders) {
try {
result = secondaryProvider.authenticate(authentication);
if (result.isAuthenticated()) {
break;
}
} catch (AuthenticationException e1) {
exception = e;
}
}
}
if (result == null || !result.isAuthenticated()) {
throw exception;
}
return result;
}
I have global exception handler as shown below.
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({NoPermissionException.class})
#ResponseBody
#ResponseStatus(value = HttpStatus.FORBIDDEN)
public Map<String, String> noPermission(NoPermissionException e) {
return createErrorResponse(e, "Don't have permissions");
}
#ExceptionHandler({Exception.class})
#ResponseBody
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, String> exceptionInProcessing(Exception e) {
return createErrorResponse(e, "Unable to process. Unknown error occurred: " + e.getMessage());
}
private Map<String, String> createErrorResponse(Exception e, String errorMessage) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("message", errorMessage);
errorResponse.put("reason", e.toString());
return errorResponse;
}
}
When exception is thrown inside the 'authenticate' function, global exception handler is not being called. For all the other exceptions it is being called. I want to catch the exception inside global exception handler and return custom error message. How can I do that? Any help appreciated. Thanks in advance.
The GlobalExceptionHandler is for controller exception handler, but the AuthenticationProvider is still in filter, if you want to handler the AuthenticationException, you need to handle it to implement AuthenticationEntryPoint and override the commence method.
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException
AuthenticationException and AccessDeniedException have already been handled by ExceptionTranslationFilter. You just need to inject AuthenticationEntryPoint and AccessDeniedHandler(which handle AccessDeniedException)
Or you can catch these exception in filter and then handle it in filer, like AuthenticationFailureHandler in AbstractAuthenticationProcessingFilter
To complement the #chaoluo answer:
Implement the AuthenticationEntryPoint interface and resolve the exception by HandlerExceptionResolver:
#Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint, AccessDeniedHandler {
#Autowired
private HandlerExceptionResolver resolver;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
resolver.resolveException(request, response, null, exception);
}
}
Inject the RestAuthenticationEntryPoint into your WebSecurityConfigurerAdapter implementation and use it as the authenticationEntryPoint:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Now because we resolved the exception by HandlerExceptionResolver, we can use the typical Spring Web error handling using #ControllerAdvice and #ExceptionHandler annotations:
#RestControllerAdvice
public abstract class ErrorsControllerAdvice {
#ExceptionHandler
public ResponseEntity<?> handleException(Throwable exception, WebRequest webRequest, Locale locale) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
}
}
Authentication provider is called before controller exception handler has a chance to catch exceptions.
You can override AuthenticationFailureHandler to handle exceptions on security filter chain level, look at the examples
The behavior as described in documentation:
The filter calls the configured AuthenticationManager to process each authentication request. The destination following a successful authentication or an authentication failure is controlled by the AuthenticationSuccessHandler and AuthenticationFailureHandler strategy interfaces, respectively. The filter has properties which allow you to set these so you can customize the behaviour completely
As #chaoluo already said you need to implement AuthenticationEntryPoint and override the commence method. If you want to return an error JSON object you can do the following:
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//create errorObj
PrintWriter writer = response.getWriter();
mapper.writeValue(writer, errorObj);
writer.flush();
}

Securing a REST Api with Spring Security

I'm new to Spring Security and I'm trying to secure a REST api inside my app. I have the app URIS and then I have an URI like "/api/v1/" to my rest api.
I have secured my application with username/password authentication and it's working fine, but now I want to secure my REST api returning 401 Unauthorized if the user isn't authenticated, but I don't know how to keep the two authentications together.
What it's the way to go here?
PS: I'm using Spring MVC with Spring Security
This is my Spring Security configuration right now:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountService accountService;
#Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", accountService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(true).userDetailsService(accountService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/favicon.ico", "/resources/**", "/signup").permitAll().anyRequest()
.authenticated()
.and().formLogin().loginPage("/signin").permitAll().failureUrl("/signin?error=1")
.loginProcessingUrl("/authenticate")
.and().logout().logoutUrl("/logout").permitAll().logoutSuccessUrl("/signin?logout")
.and().rememberMe().rememberMeServices(rememberMeServices()).key("remember-me-key")
.and().csrf();
}
#Bean(name = "authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
So you want to have form login plus secure rest api right?
jhipster can generate such project structure. Let me give some example code for achieving this if you don't want to use jhipster (it's pretty cool though, I recommend)
to return 401 for unauthorized you need something like this:
/**
* Returns a 401 error code (Unauthorized) to the client.
*/
#Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(Http401UnauthorizedEntryPoint.class);
/**
* Always returns a 401 error code to the client.
*/
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
throws IOException,
ServletException {
log.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
then inject and add this to your SecurityConfig.configure(HttpSecurity http) method:
http.authenticationEntryPoint(authenticationEntryPoint)
plus since REST requests are ajax requests you also need ajax entry points:
/**
* Returns a 401 error code (Unauthorized) to the client, when Ajax authentication fails.
*/
#Component
public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}
/**
* Spring Security success handler, specialized for Ajax requests.
*/
#Component
public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
/**
* Spring Security logout handler, specialized for Ajax requests.
*/
#Component
public class AjaxLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
implements LogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
add them to security config too:
http.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
All credit goes to the amazing jhipster authors.
You can use expression based access control
http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
Something like,
<intercept-url pattern=/** access="isFullyAuthenticated()/>

How to handle spring security InternalAuthenticationServiceException thrown in Spring ProviderManager

ProviderManager is throwing InternalAuthenticationServiceException.class while retrieving users in DaoAuthenticationProvider.class,
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
I want to handle this exception and return my custom response to the client.
I don't want to handle this by writing custom ProviderManager.
For all other OAuth exceptions i am able to handle the exceptions using Custom WebResponseExceptionTranslator.
But I am unable to catch security exceptions like InternalAuthenticationServiceException.class.
I don't have option to use ErrorController with the /error path, it is breaking other flows.
You can write a class which is annotated with #ControllerAdvice and have a #ExceptionHandler(value=InternalAuthenticationServiceException.class).
Ex:-
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(InternalAuthenticationServiceException.class)
public ResponseEntity<String> handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {
ResponseEntity<String> response = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
}
UPDATE
If you don't have controllers and using #EnableAuthorizationServer then you need to extend from AuthorizationServerConfigurerAdapter and override configure(AuthorizationServerEndpointsConfigurer endpoints) as below. You can use AuthorizationServerEndpointsConfigurer.exceptionTranslator to handle your InternalAuthenticationServiceException.
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// other endpoints
.exceptionTranslator(e -> {
if (e instanceof InternalAuthenticationServiceException) {
InternalAuthenticationServiceException internalAuthenticationServiceException = (InternalAuthenticationServiceException) e;
// return a ResponseEntity or throw a custom Exception.
}
});
}
First you need to implements your own AuthenticationEntryPoint the name is not really autoexplicative...
For example if you need to return always status code 200 (only for learning purpose, please donĀ“t do it in real world...)
#Component("myOwnAuthenticationEntryPoint")
public class MyOwnAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_OK, "Unauthorized");
}
Then in your WebSecurityConfig you need to set it as your authentication exception handler entry point.
...
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Autowired
MyOwnAuthenticationEntryPoint myOwnAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(myOwnAuthenticationEntryPoint);
...
}
Thats all. :)
I've solved that problem by override unsuccessfulAuthentication method in my filter and send an error response to the client with the desired HTTP status code. In my case, I also created my custom exception (RecordNotFoundException) that is thrown from my service.
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
if (failed.getCause() instanceof RecordNotFoundException) {
response.sendError((HttpServletResponse.SC_NOT_FOUND), failed.getMessage());
}
}

How to disable or override RequestCacheAwareFilter in Spring Boot

I have very basic simple Spring Boot Rest application.
I needed to implement custom authentication in Spring Security: for every REST request I need to check username and password, that are in specific headers of every request ("username" and "password").
So I implemented custom AuthEntryPoint:
#Service
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
String username = httpServletRequest.getHeader("username");
String password = httpServletRequest.getHeader("password");
if (!username.equals("admin") || !password.equals("admin")) {
throw new RuntimeException("", new BadCredentialsException("Wrong password"));
}
}
}
So, I realized, that RequestCacheAwareFilter is caching first request and headers are also stored in cache. So if I make a request with wrong pass and then with right one, I will still get an exception.
So, how could I override the CacheAwareFilter or disable it? Or am I doing something totally wrong?
Use custom WebSecurityConfigurerAdapter to set request cache to NullRequestCache:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestCache()
.requestCache(new NullRequestCache());
}
}
I just made the app stateless like here: How can I use Spring Security without sessions?
And now everything is okay.

Categories

Resources