I am using keycloak authentication with java. The keycloak java adapter throws exceptions in case of invalid tokens etc. Now, I want to catch these exceptions so that I can show custom message and do other stuffs. In the below example I tried to catch exceptions by creating a filter but was unsuccessful.
E.g.:
DEBUG TRACE:
In the following trace, AuthentciatedActionHandler.isAuthorised method throws exception in case of invalid token.
I thought that may be adding a filter with Priority(1) will introduce the filter somewhere in the stack and I can handle the exceptions thrown by keycloak in the filter. But as you can see, filter does not come in stack calls.
(Although in case of valid request, AuthMasterFilter comes in stack trace)
My Jetty config:
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY ); //keycloak
context.setContextPath("/");
context.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
context.addFilter(AuthMasterFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); // filter to handle exceptions from keycloak
ResourceConfig resourceConfig = new ResourceConfig(ImmutableSet.<Class<?>>builder().add(KeycloakRestService.class).build());
context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
KeycloakJettyAuthenticator kcAuthenticator = ... // set up keycloak
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setAuthenticator(kcAuthenticator);
context.setSecurityHandler(securityHandler);
AUTHMASTERFILTER.java:
#Priority(1)
public class AuthMasterFilter implements Filter{
#Override
public void destroy() { }
#Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
System.out.println("Master called");
}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Related
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);
}
....
One more question about spring configuration...
I have several rest methods opened to everyone and not secured. These rest methods on server #1 are used by another server #2 in the same domain to get some data. The idea is that server #2 sets my_super_secure_cookie to some secure token and server #1 decodes and verifies it. Here is the code:
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// Some code
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/rest/public/*").permitAll()
.anyRequest().authenticated();
}
// More code
}
public class SuperSecurityFilter extends FilterSecurityInterceptor implements Filter {
public SuperSecurityFilter(String key) {
super(key);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
Cookie[] cookies = req.getCookies();
Optional<Cookie> tokenCookie = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("my_super_secure_cookie")).findFirst();
if (tokenCookie.isPresent()) {
Cookie cookie = tokenCookie.get();
TokenCookie.create(cookie.getValue()).validate();
} else {
throw new Exception("Ooops!"));
}
chain.doFilter(req, res);
}
}
The question is how do I configure SecurityConfig to use SecurityTokenFilter on request to any of the /rest/public/* rest methods. Something like:
http
.antMatcher("/rest/public/*")
.addFilterBefore(new SuperSecurityFilter());
is not working, SuperSecurityFilter is not called on request.
p.s. I'm forced to work with this type of security model due to current business logic restrictions.
I solved (applied workaround?) the issue I have by implementing not filter, but interceptor, like this:
public class SuperSecurityInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// implementation here
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// Nothing here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Nothing here
}
}
And registered this interceptor in my entity extending WebMvcConfigurerAdapter. Like this:
registry.addInterceptor(new SupeSecurityInterceptor()).addPathPatterns("/rest/public/*");
Not sure if this is right thing to do though... Anyway would be glad to know about the conventional approach of implementing this type of functionality.
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());
}
}
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)
}
}
}
Whenever a client aborts the connection, I'm getting a ClientAbortException logged as follows:
org.apache.catalina.connector.ClientAbortException: java.io.IOException: APR error: -730053
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:353) ~[catalina.jar:8.0.26]
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:317) ~[catalina.jar:8.0.26]
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:110) ~[catalina.jar:8.0.26]
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1022) ~[jackson-core-2.6.5.jar:2.6.5]
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:891) ~[jackson-databind-2.6.5.jar:2.6.5]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:264) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Question: as I don't care if the client aborts the connection, I'd want to prevent logging. Or moreover, prevent my application trying to acutally return a response.
How could I do this?
Could I create some kind of global #ExceptionHandler(ClientAbortException.class) but returning void if I catch any?
Since at least Spring Boot 2.3.4 (and probably before) you can use a #ControllerAdvice annotated class with the following method:
#ExceptionHandler(ClientAbortException.class)
public void handleLockException(ClientAbortException exception, HttpServletRequest request) {
final String message = "ClientAbortException generated by request {} {} from remote address {} with X-FORWARDED-FOR {}";
final String headerXFF = request.getHeader("X-FORWARDED-FOR");
log.warn(message, request.getMethod(), request.getRequestURL(), request.getRemoteAddr(), headerXFF);
}
I've had the same problem and i was unable to do what you tell with Spring MVC and a Exception handler. Some exceptions (Unchecked ones i guess) are not chatched by Spring MVC handlers. What i did was to define a generic filter in web.xml
<!-- Filter for exception handling, for those exceptions don't catched by Spring MVC -->
<filter>
<filter-name>LoggerFilter</filter-name>
<filter-class>com.myproject.commons.filters.ExceptionLoggerServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And the source of my filter:
public class ExceptionLoggerServletFilter implements Filter {
private static Log log = LogFactory.getLog(ExceptionLoggerServletFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
try {
chain.doFilter(request, response);
} catch (Throwable e) {
StringBuilder sb = new StringBuilder();
sb.append("Exception detected in ExceptionLoggerServletFilter:");
if (e instanceof org.apache.catalina.connector.ClientAbortException) {
// don't do full log of this error
sb.append(" ClientAbortException");
log.error(sb.toString());
} else {
log.error(sb.toString(), e);
}
throw e;
}
}
#Override
public void destroy() {
}
}