I have a created a class JwtAuthenticationFilter that includes this method:
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
Authentication authentication = null;
if(hasJsonToken(request)) {
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(getJsonToken(request));
authentication = getAuthenticationManager().authenticate(jwtAuthenticationToken);
} else {
throw new AuthenticationCredentialsNotFoundException(AUTHENTICATION_CREDENTIALS_NOT_FOUND_MSG);
}
return authentication;
}
If no JWT has been supplied an AuthenticationCredentialsNotFoundException is thrown. I would expect this to trigger the commence method in my AuthenticationEntryPoint - which looks like this:
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpStatus.UNAUTHORIZED.value(),HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
The commence method is not being call. This in my spring security config (or part of it):
@Bean
public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint()).and()
.csrf().disable()
.authorizeRequests().antMatchers(AUTHORISED_SERVICE_REQUESTS_ANT_MATCHER).authenticated()
.anyRequest().permitAll();
}
Not sure what I'done wrong here and I'm hoping someone point it out to me. Thanks
My SecurityConfig class extends WebSecurityConfigurerAdapter and is annotated with @Configuration and @EnableWebSecurity
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
I am using spring boot.
So... eventually I got the behaviour I wanted by creating a custom AuthenticationFailureHandler and registering it in my Authentication F.ilter
jwtAuthenticationFilter.setAuthenticationFailureHandler(new JwtAuthenticationFailureHandler());
My question now is, is this the right thing to do and what is the difference between an AuthenticationEntryPoint and an AuthenticationFailureHandler?
This is my code to implement AuthenticationEntryPoint and AccessDeniedHandler with Spring Boot / JWT. I hope it will be of help to anyone.
AuthenticationEntryPoint
@Component
public class AuthenticationEntryPointJwt implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authenticationException) throws IOException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
final Map<String, Object> body = new HashMap<>();
body.put("code", HttpServletResponse.SC_UNAUTHORIZED);
body.put("payload", "You need to login first in order to perform this action.");
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(httpServletResponse.getOutputStream(), body);
}
}
AccessDeniedHandler
@Component
public class AccessDeniedHandlerJwt implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
final Map<String, Object> body = new HashMap<>();
body.put("code", HttpServletResponse.SC_FORBIDDEN);
body.put("payload", "You don't have required role to perform this action.");
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(httpServletResponse.getOutputStream(), body);
}
}
WebSecurityConfigurerAdapter
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private Environment env;
@Autowired
private SecurityUserDetailsService securityUserDetailsService;
@Autowired
private SecurityRequestFilter securityRequestFilter;
@Autowired
private AuthenticationEntryPointJwt authenticationEntryPointJwt;
@Autowired
private AccessDeniedHandlerJwt accessDeniedHandlerJwt;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(securityUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
if (Boolean.parseBoolean(env.getRequiredProperty("security.disable.csrf")))
httpSecurity.csrf().disable();
httpSecurity
.httpBasic().disable()
.formLogin().disable()
.authorizeRequests()
.antMatchers(env.getRequiredProperty("security.uri.white-list").split(",")).permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPointJwt).and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandlerJwt).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(securityRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
You can check the complete code on my GitHub
https://github.com/JonathanM2ndoza/Nginx-Docker-Spring-Boot/tree/master/security/src/main/java/com/jmendoza/wallet/security
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With