问题
I have a spring boot rest API project and I am pondering how to change the default error object returned from spring boot.
UseCase: /token api is to be called without authentication and other apis are to be called by passing a token. The swagger UI needs to be used for the same purpose. Error response needs to be modified as a custom object.
eg: The default structure is like this for Unauthorised api request.
{
"timestamp": "2020-06-14T05:46:37.538+00:00",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/scholarship/api/v1.0/applicant"
}
I would want it to be like
{
"status": "error",
"message": "Unauthorised"
}
I have swagger ui also configured for the project.
My Config file is like this which helps me open the swagger-ui. html and i have also tried to get a handle to the Unauthorised abject in the exception handler in this but it doesn't fire.
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/api/**")
);
AuthenticationProvider provider;
public SecurityConfig(final AuthenticationProvider authenticationProvider) {
super();
this.provider = authenticationProvider;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
@Override
public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/token/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
@Bean
AuthenticationFilter authenticationFilter() throws Exception {
log.error("in authenticationFilter");
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
//filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
public AuthenticationEntryPoint authenticationEntryPoint() {
log.error("in authenticationEntryPoint");
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
log.error("in commence");
try {
log.error(authException.getLocalizedMessage());
handlerExceptionResolver.resolveException(request, response, null, authException);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
};
}
}
Alternatively, i have a @ControllerAdvice to reformat the error object but it is also not working
@ExceptionHandler(value = {InsufficientAuthenticationException.class})
public final ResponseEntity<Object> authenticationException(InsufficientAuthenticationException ex) {
List<String> details = new ArrayList<>();
details.add("Authentication is required to access this resource");
ErrorResponse error = new ErrorResponse("error", "Unauthorized", details);
return new ResponseEntity(error, HttpStatus.FORBIDDEN);
}
What could be possibly misconfiguration?
Update1:
If I change to this then I get my custom object when invoking the secure api, but swagger Ui is not opening and I get my custom error for swagger UI access
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.fullyAuthenticated();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
Error when accessing Swagger UI
{"status":"error","message":"Unauthorized","errors":["Authentication is required to access this resource"]}
Update 2:
Changing to the below opens up the Swagger UI but again the error objects are not custom then
@Override
public void configure(HttpSecurity http) throws Exception {
log.error("in configure");
http.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.fullyAuthenticated();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
Update 3:
The core issues lie's in configure(HttpSecurity http) method
If i add the below two lines the API's are authenticated but the error object is the default object. If I remove these lines the API are not authenticated(even if you pass the token they keep failing) but I get the custom error object.
.authenticationProvider(provider)
.addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
回答1:
The exceptions from Servlet Filter which do not reach the controller are handled by
BasicErrorController.
You need to override the BasicErrorController to change the default exception body thrown by spring.
How to override:
@RestController
@Slf4j
public class BasicErrorControllerOverride extends AbstractErrorController {
public BasicErrorControllerOverride(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = this.getStatus(request);
/*
If you want to pull default error attributes and modify them, use this
Map<String, Object> defaultErrorAttributes = this.getErrorAttributes(request, false);
*/
Map<String, Object> errorCustomAttribute = new HashMap<>();
errorCustomAttribute.put("status", "error");
errorCustomAttribute.put("message", status.name());
return new ResponseEntity(errorCustomAttribute, status);
}
@Override
public String getErrorPath() {
return "/error";
}
}
How error response will look like
{
"message": "UNAUTHORIZED",
"status": "error"
}
来源:https://stackoverflow.com/questions/62368896/custom-error-objects-for-spring-rest-api-and-authentication