I'm trying to configure some custom exception handling with Spring Security 3.1.2. I tried following the examples I found here and here, but neither works. I'm new to Spring Security, and I'm wondering if this might have something to do with the fact that I'm using a preauth filter. I'm throwing my custom exceptions from within the loadUserDetails() method of my AuthenticationUserDetailsService implementation.
public class AuthServiceImpl implements AuthenticationUserDetailsService<Authentication> {
#Autowired
private AuthDao authDao;
#Override
public UserDetails loadUserDetails(Authentication auth) throws UsernameNotFoundException {
Request req = (Request) auth.getPrincipal();
//get user details
User u = authDao.loadUser(req.getSessionId());
//check user rights for requested action
if(!u.getRights().contains(req.getAction()){
throw new CustomAuthException("User does not have permission to perform this action");
}
return u;
}
}
When the exception is thrown I just get the normal Tomcat 500 page with the exception details. For whatever reason my custom exceptions are not getting handled at all. I even added some println()s in the custom handler, and it's not even being called.
I'm starting to wonder if this method is somehow excluded from Spring's exception handling. I can provide more code examples if needed, but at this point I'm not sure what would be relevant to share.
You use SimpleMappingExceptionResolver. It is a Spring MVC component. So when you have some exception during execution of some controller then DispatcherServlet will call SimpleMappingExceptionResolver.
The problem is that your AuthenticationUserDetailsService implementation is used only during login action. And this action is processed by Spring Security filter directly (Spring MVC is not used). Request does not reach DispatcherServlet and SimpleMappingExceptionResolver will never be called for this case.
Related
I create REST web-service with Spring Boot.
I would like to know what is a better way to handle exceptions in a controller. I have seen other questions and didn’t found an answer.
My controller:
#GetMapping
public ResponseEntity<?> saveMyUser(){
MyUser myUser = new MyUser(“Anna”);
//throws SQLException
MyUserDetails userDetails = userService.saveMyUser(myUser);
//if successful
return ResponseBody.ok(userDetails);
}
saveMyUser() method of UserService:
public MyUserDetails saveUser(MyUser) throws SQLException {...}
So at this point I have at least 2 simple options:
Add exception to method signature.
Here I may rely on Spring Boot to pass all information about exception and status code to a client. However do not know if it is a reliable approach.
Surround with try/catch and pass all information about exceptions manually.
What is a better simple way?
You can create an additional class with #ControllerAdivce annotation and later you will be able to write custom response logic for each exception e.g:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({SQLException.class})
public ResponseEntity<Object> sqlError(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Some SQL exception occured");
}
}
Also, you can extend ResponseEntityExceptionHandler and override the default behavior for mapping from exceptions to HTTP response.
Also, take a look at this, it holds very usefull information for your case.
I'm trying to throw a custom exception in WebFlux during authentication, and handle it with a ControllerAdvice (#ExceptionHandler). Unfortunately, it doesn't get propagated, I'm getting either HTTP 500 if I throw the exception, or HTTP 401 if I return the exception as Mono.error()
#Override //in authentication service
public Mono<UserDetails> findByUsername(String username) {
//find user by username from database,
//if not enabled, throw a custom exception,
//if doesn't exist, throw UsernameNotFoundException,
//return org.springframework.security.core.userdetails.User otherwise.
}
#ExceptionHandler //in controller advice
public Mono<HttpStatus> handleException(MyCustomExceptionThrownFromFindByUsername ex) {
//implemented
}
Is there any way to help the exception to make it to the ExceptionHandler?
The UserDetailsService (both reactive and non-reactive) has as a job to retrieve the user based on the username. Nothing more and nothing less. Checking if the user is enabled is delegated to a UserDetailsChecker which calls some methods on the UserDetails implementation and will react accordingly. Don't try to do more in here as that isn't the task of the UserDetailsService.
The default UserDetails, User implementation has 2 constructors, one with 3 and one with 7 parameters. Use the second one to fill the enabled properly according to your business rules and Spring Security will do the rest as it should.
I have custom security filter which serves as additional authorization step.
The filter checks if a user can be authorized and throws an exception if the user is not supposed to access the resource.
The problem is that if I throw an exception from the filter - it doesn't get mapped to correct status code (in my case I need HTTP 403).
I can't use #ControllerAdvice and #ExceptionHandler because security filters work before controller handling.
I thought may be I'm doing it wrong and I shouldn't throw an exception, or I should throw a very specific one.
Q: Is there any way to automatically map exceptions from filters to proper status codes? Or is there a way to implement the filter without exceptions?
Note: I also read this post, but from debug I see that my filter chain doesn't contain ExceptionTranslationFilter.
Or is there a way to implement the filter without exceptions?
Obviously, you can directly write to response and return from the very point you catch an authentication or authorization failure. Throwing exceptions then globally handling it seemed too much unnecessary overwork for me since I had only one JWT authentication filter implementing - AbstractAuthenticationProcessingFilter
Whenever an authentication condition is not met , like JWT token parsing issue, missing token , malformed token etc , I simply log error , set HttpStatus and return null from attemptAuthentication method.
This way my whole logic is encapsulated in single class. I also had to send 401 for all cases but error messages were different and that was handled by a simple utility method.
I don't find any fault in throwing and then handling exceptions if you have to do that from many places in your application and in that case handler could be specified as in Nicholas Smith's answer.
The Spring recommended way is to have a #Component that implements AccessDeniedHandler and then in your class that extends WebSecurityConfigurerAdapter register it to the .accessDeniedHandler() in the configure(HttpSecurity ...) method. That's for 403, if you want 401 errors to be wrapped as well then you need to extend Http401AuthenticationEntryPoint. I'll focus on 403 cases below.
Your WebSecurityConfigurerAdapter
#Override
protected void configure(final HttpSecurity http) throws Exception {
...
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(<YOUR Http401AuthenticationEntryPoint>)
.and()
.exceptionHandling().accessDeniedHandler(<YOUR ACCESS DENIED HANDLER>);
...
}
Your AccessDeniedHandler
#Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setHeader("Content-Type", "application/hal+json;charset=UTF-8");
response.getOutputStream()
.print(objectMapper
.writeValueAsString("Access Denied");
response.setStatus(403);
}
I am writing an LTI application using Spring boot. LTI applications are basically a plug-in for a learning management system (in this case Canvas) which work by sending an Oauth1 signed POST to my server. The result of this request is displayed to the user inside of an iframe. There is a pre-shared key and secret that the LMS uses to sign the request. If the signature on the POST checks out, I have an authenticated user. I have this part working, partially based on this question.
During the initial request (which comes to the URL "/launch") I can call SecurityContextHolder.getContext().getAuthentication() and use this without problems. My problem is when the user makes another request, say for a picture or by clicking on a link in my content, the SecurityContext object isn't following them. I'm pretty sure I'm not setting up the Spring security filter chain correctly so the SecurityContextPersistenceFilter isn't being hit on subsequent requests. At the end of the day, SecurityContextHolder.getContext().getAuthentication() returns null.
The OAuth signature verification happens in a WebSecurityConfigurerAdapter like so: (again, based on this)
#Configuration
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
//spring auto-wiring to set up the
//zeroLeggedOauthProviderProcessingFilter (see linked question)
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/launch")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}
So this works and creates an authenticated principal and everything. But due to that antMatcher, it only applies to the /launch path.
It seems like it should be simple to add another security configurer adapter that will ensure that all other paths in the application are protected by an authenticated session and in so doing would cause the SecurityContext associated with this user to become available but I have been unable to come up with the magic sauce. The documentation focuses more on standard login form based authentication setups. I'm also kind of new to Spring in general so I'm clearly missing something. I tried this but it causes all other requests to return a 403:
#Configuration
public static class SessionSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}
I have a java webapp using Spring 3.1. My Spring security context defines multiple authentication filters, each corresponding to a different authentication path (e.g. username/password vs. Single Sign On). Each auth filter defines its own AuthenticationSuccessHandler. Now, I want to inject 2 additional actions to take upon successful authentication, and they should apply across all authentication types:
set a tracking event code for Google Analytics to use on the front-end
update the user's preferred locale in our database
These could be any actions that you want a hook for, after the user has been successfully authenticated. The important point is that, unlike the regular AuthenticationSuccessHandlers (which are different for each authentication path), they don't forward or redirect the request. So it's safe to call a bunch of them.
Is there a clean way to integrate these additional authentication success "actions", using Spring Web/Security 3.1?
I looked into implementing an ApplicationListener<AuthenticationSuccessEvent>, but my events need to access the request, and all AuthenticationSuccessEvent provides is the Authentication object itself.
I couldn't find a way, so I decided to roll my own proxy:
public class AuthenticationSuccessHandlerProxy implements AuthenticationSuccessHandler {
private List<AuthenticationSuccessHandler> authenticationSuccessHandlers;
public AuthenticationSuccessHandlerProxy(List<AuthenticationSuccessHandler> successHandlers) {
this.authenticationSuccessHandlers = successHandlers;
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
for (AuthenticationSuccessHandler successHandler : this.authenticationSuccessHandlers) {
successHandler.onAuthenticationSuccess(request, response, authentication);
}
}
}
After looking breafly into the source code of AbstractAuthenticationProcessingFilter and all other places where AuthenticationSuccessHandler.onAuthenticationSuccess(...) is called I do not see any possibility to do it using Spring Security.
As a workaround you can try to wrap your success handlers into some AspectJ or AOP pointcut and then apply this pointcut to AuthenticationSuccessHandler.onAuthenticationSuccess(...) execution. Maybe like this you can target all authentication types.