error-handling-spring-boot-starter
error-handling-spring-boot-starter copied to clipboard
Add 401 support for when not authenticated (spring security)
Hello
Could you handle the case where the requester hasn't been authenticated? For the moment, Spring Security return only the 403 status.
A way that could be done is by using an AuthenticationEntryPoint
(reference):
@Component
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
protected final HttpStatusMapper httpStatusMapper;
protected final ErrorCodeMapper errorCodeMapper;
protected final ErrorMessageMapper errorMessageMapper;
protected final ObjectMapper objectMapper;
@Autowired
public UnauthorizedEntryPoint(HttpStatusMapper httpStatusMapper, ErrorCodeMapper errorCodeMapper, ErrorMessageMapper errorMessageMapper) {
this(httpStatusMapper, errorCodeMapper, errorMessageMapper, new ObjectMapper());
}
public UnauthorizedEntryPoint(HttpStatusMapper httpStatusMapper, ErrorCodeMapper errorCodeMapper, ErrorMessageMapper errorMessageMapper, ObjectMapper objectMapper) {
this.httpStatusMapper = httpStatusMapper;
this.errorCodeMapper = errorCodeMapper;
this.errorMessageMapper = errorMessageMapper;
this.objectMapper = objectMapper;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws JsonProcessingException, IOException {
ApiErrorResponse errorResponse = createResponse(authException);
response.setStatus(errorResponse.getHttpStatus().value());
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
}
public ApiErrorResponse createResponse(AuthenticationException exception) {
HttpStatus httpStatus = httpStatusMapper.getHttpStatus(exception, HttpStatus.UNAUTHORIZED);
String code = errorCodeMapper.getErrorCode(exception);
String message = errorMessageMapper.getErrorMessage(exception);
return new ApiErrorResponse(httpStatus, code, message);
}
}
Though, I didn't managed to make it auto-configurable....
The only way is to use http.exceptionHandling().authenticationEntryPoint(...)
:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private UnauthorizedEntryPoint unauthorizedEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
/* used for the next example */
http.authorizeHttpRequests()
.antMatchers("/insecure", "/secure").permitAll()
.anyRequest().authenticated();
http.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint);
}
}
And testing this method using a basic controller give the expected results:
@RestController
public class IndexController {
@GetMapping("/insecure")
public String insecure() {
return "should be ok";
}
@PreAuthorize("authenticated")
@GetMapping("/secure")
public String secure() {
return "should raise an access denied exception";
}
@GetMapping("/authenticated")
public String authenticated() {
return "should raise an insufficient authentication exception";
}
}
What do you think?