Throw and handle custom exception during authentication Spring Security + WebFlux - java

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.

Related

Handle exceptions in a rest controller in Spring Boot

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.

How to correctly handle pre-authentication failures in Spring Security?

I'm implementing an application that has a variety of ways to login, most importantly via a request header for pre-auth purposes and normal username/password. Currently everything works, but I've been requested to redirect the user to an error page if they attempted to use the pre-auth filter and it "failed".
From what I can tell, if pre-auth returns null it just assumes non-authed and continues the flow. I'm looking for a way to redirect or raise an exception, but I can't seem to hit my AuthenticationFailureHandler from the pre-auth filter. Is there a better way to do this? Do I need to hack something together that optionally treats the principal as an Exception. Should I be using a different authentication mechanism?
I'm by no means am Spring Security expert, so any help is welcome.
I cannot share the code due to contractual agreements, but here's an example:
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
Object principal = null;
// Try login stuff if token exists...
String token = request.getHeader("the-token-key");
if (token != null && !token.empty()) {
...
// If successful, correctly set principal
principal = ...;
// Else login failed, redirect or throw exception??
}
return principal;
}

Check extra parameters with Spring Security

Please give a hint in Spring Security, how can I check additional parameters during user login.
For example, to check not only "username" and "password", but also if he confirmed his registration with email link;
All data is stored in DB and i can get it easily with my implementation of UserDetailsService;
But how to make the security service to pay attention to the additional parameter "isValidated"?
I'm using Spring 3.2.0 now;
One way to achieve this is to create a custom AuthenticationProvider or to extend an existing one. In your case it might be sufficient to extend for instance the DaoAuthenticationProvider and put the logic for checking whether the account is confirmed in additionalAuthenticationChecks() method.
Here is an example:
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// Perform the checks from the super class
super.additionalAuthenticationChecks(userDetails, authentication);
// Cast the UserDetails to the implementation you use
User user = (User) userDetails;
// Check the confirmed status
if (!user.isAccountConfirmed()) {
throw new AccountNotConfirmedException("Account is not confirmed yet.");
}
}
public static class AccountNotConfirmedException extends AuthenticationException {
public AccountNotConfirmedException(String message) {
super(message);
}
}
}
Your implementation of UserDetails should contain the information about account confirmation status. You can map this information in your implementation of UserDetailsService.
Option 2
Edit: Now that I look at it, the first solution is a bit overcomplicated. You can easily solve this problem without using custom AuthenticationProvider. Just make sure that isEnabled() of your UserDetails implementation returns false if the account is not confirmed. If the enabled property is false authentication will not be allowed (this is automatically taken care of by Spring Security).
The first solution might still be useful if you want explicitly handle the AccountNotConfirmedException in AuthenticationFailureHandler for instance.

how to use Spring security with Custom java class/ custom spring framework's class?

My requirement is as below:
In our application the user's credentials are validated against the database(not using spring security since it is a legacy application) for the first time. If the user is a valid user, he will be logged into the application. Once the user logs into the application he can make few rest calls. Now, I want to once again validate the user's credentials by using spring security before making any rest call. Here, the challenge is we should not redesign the database schemas. We need to use a stored procedure which validates the user. This particular stored procedure returns an error message if authentication fails, otherwise nothing is returned. There are no roles defined in this case. Just simple authentication using a stored procedure. Now, I want to accomplish this whole thing by spring security. May be writing a java class/ custom spring framework's class and in which the stored procedure is called and using that class in spring security configuration files. Can anybody suggest ideas on how to start up with please?
I have implemented AuthenticationProvider. The following is the *security.xml.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/rest/*" access="permitAll"></intercept-url>
</http>
<authentication-manager >
<authentication-provider ref="csAuthenticationProvider" />
</authentication-manager>
But, the security framework is looking for roles. In my case there are no roles defined. As I said earlier, the user is authenticated for the first time without using spring framework. If the user wants to make any rest call, the spring security needs to re authenticate the user. It doesn't mean that the user needs to re enter credentials. The user's credentials are available in the rest call/request since he is already authenticated. The only thing needs to be done is I need to use the credentials by using request and re validate using the stored procedure. of course, using AuthenticationProvider may be a good idea, but the parameter "Authentication authentication" of the authenticate(Authentication authentication) method is not useful for me since I need to call my own stored procedure call again. for time being, I did not used the Authentication object, but instead used the stored procedure calling code in the authenticate() method. But, strangely, authenticate() method is not getting called. I am surprised and in confusion. Does any body has any ideas on where I am doing wrong?
Sounds like you need to implement an Authentication Provider. Here's a pretty simple example that I think you could adapt to call your stored procedure.
http://danielkaes.wordpress.com/2013/02/20/custom-authentication-provider-in-spring/
You can implement your own UserDetailsService and configure spring to use it.
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsServiceImpl"/>
</security:authentication-manager>
You need to create a custom UserDetailsService implementation, that will check against the DB.
Here is an example UserDetailsService implementation that does just that:
#Service("userService")
public class UserDetailsServiceImpl implements UserDetailsService, InitializingBean {
#Autowired
private AccountService accountService;
public void afterPropertiesSet() throws Exception {
}
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
username = username.toLowerCase();
try {
Account account = accountService.loadUserAccountByEmail(username);
if (account == null) {
throw new UsernameNotFoundException("Could not find email: " + username + "in the DB.");
}
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
for (Role r : account.getRoles()) {
auths.add(new SimpleGrantedAuthority(r.getRole()));
}
ApplicationUser user = null;
try {
user = new ApplicationUser(new Long(account.getId()), username, account.getPassword(), true, true, true, true, auths);
} catch (Exception ex) {
ex.printStackTrace();
}
return user;
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException(username + "not found", e);
}
}
}
Which I config in code like so:
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsServiceImpl)
.passwordEncoder(bCryptPasswordEncoder());
}
(you can also see a blog post I wrote about switching from xml to #annotation config for spring security referncing that project here: http://automateddeveloper.blogspot.co.uk/2014/02/spring-4-xml-to-annotation-configuration.html)

Spring security custom exception handler with preauth

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.

Categories

Resources