'Spring Security with Java Configuration: How to handle BadCredentialsException from a custom provider
I need to authenticate some rest services using a token id in the url (or maybe in the request header - but this is not important for now). I am trying to use java configuration to set this up using as a guide this post. My problem is that I do not know how to handle "BadCredentialsException" that is thrown when the authentication fails from the provider. Here is my Security Config:
public static class SecurityConfigForRS extends
WebSecurityConfigurerAdapter {
@Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.regexMatcher("^/rest.*")
.addFilterBefore(
new TokenAuthenticationFilter(
authenticationManagerBean()),
AbstractPreAuthenticatedProcessingFilter.class)
.and().csrf().disable();
}
}
For now I skip the other implementations - if it helps I will post them later.
When the token is missing or is invalid, the TokenAuthernticationProvider
throws a BadCredentialsException
. I need to catch this and send back an 401-Unauthorized
. Is it possible to do this?
Solution 1:[1]
The first Filter I created was a subclass of GenericFilterBean and it did not have support for authentication failure handler or success handler. However AbstractAuthenticationProcessingFilter supports success and failure handlers. My filter is as simple as that:
public class TokenAuthenticationProcessingFilter extends
AbstractAuthenticationProcessingFilter {
public TokenAuthenticationProcessingFilter(
RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException,
IOException, ServletException {
Authentication auth = new TokenAuthentication("-1");
try {
Map<String, String[]> params = request.getParameterMap();
if (!params.isEmpty() && params.containsKey("auth_token")) {
String token = params.get("auth_token")[0];
if (token != null) {
auth = new TokenAuthentication(token);
}
}
return this.getAuthenticationManager().authenticate(auth);
} catch (AuthenticationException ae) {
unsuccessfulAuthentication(request, response, ae);
}
return auth;
}}
and my http security is:
public static class SecurityConfigForRS extends
WebSecurityConfigurerAdapter {
@Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
@Bean
protected AbstractAuthenticationProcessingFilter getTokenAuthFilter()
throws Exception {
TokenAuthenticationProcessingFilter tapf = new TokenAuthenticationProcessingFilter(
new RegexRequestMatcher("^/rest.*", null));
tapf.setAuthenticationManager(authenticationManagerBean());
return tapf;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.regexMatcher("^/rest.*")
.addFilterAfter(getTokenAuthFilter(),
BasicAuthenticationFilter.class).csrf().disable();
}
}
The filter chain order does matter! I placed it after BasicAuthenticationFilter and it works fine. Of course there might be a better solution but for now this works!
Solution 2:[2]
May be you can try with Global Exception handling with ControllerAdvice
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler // when Invalid Credentials
public ResponseEntity<ErrorMessage> handleInvalidCredentialsException(
BadCredentialsException ex) {
return new ResponseEntity<ErrorMessage>(
new ErrorMessage(ex.getMessage()), HttpStatus.UNAUTHORIZED);
}
@Getter
@Setter
@AllArgsConstructor
class ErrorMessage {
private String error;
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ampofila |
Solution 2 | Devang Jayswal |