3

I have this response when it comes to check the user Unauthorized.

i there any possibility to remove the Path from the Unauthorized response ? since it does not gives valuable information for the user

{
"timestamp": "2021-03-18T09:16:09.699+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/test/v1/api/test.com/config/settings"

}

this is how my config looks like

public class ResourceConfig extends ResourceServerConfigurerAdapter {


@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        .csrf().disable()
        .cors();

    httpSecurity
        .anonymous().disable()
        .requestMatchers().antMatchers("/api/**")
        .and()
        .authorizeRequests()
        .antMatchers("/api/**")
        .authenticated()
        .and()
        .exceptionHandling()
        .accessDeniedHandler(new OAuth2AccessDeniedHandler());

}
Catalina
  • 663
  • 5
  • 20
  • You could override the default authentication entry point. https://stackoverflow.com/questions/66630963/getting-wrong-exception-response-403/66632676#66632676 – linhx Mar 18 '21 at 09:44
  • I would suggest creating your generic response bean class with the desired properties and follow https://stackoverflow.com/a/52661992/5404976 – Vinit Solanki Mar 19 '21 at 06:54

1 Answers1

4

Adding on @linhx idea of using custom AuthenricationEntryPoint, you can use HandlerExceptionResolver which resolves to a page.

You can get a detailed comparison of different approaches here.

@Component
public class ABAuthenticationEntryPoint implements AuthenticationEntryPoint {

    protected final Logger logger = LoggerFactory.getLogger(ABAuthenticationEntryPoint.class);

    private final String realmName = "CustomRealm";

     @Autowired
     @Qualifier("handlerExceptionResolver")
     private HandlerExceptionResolver resolver;
     
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        resolver.resolveException(request, response, null, authException);
    }
}

The HandlerExceptionResolver uses the handler (HandlerMethod) to obtain the Controller class and scan it for methods annotated with @ExceptionHandler. If one of this methods matches the exception (ex) then this methods get invoked in order to handle the exception. (else null get returned signaling that this exception resolver feels no responsible).

So, add a class with @ControllerAdvice:

@ExceptionHandler(value = InsufficientAuthenticationException.class)
public ResponseEntity<Object> handleInsufficientAuthenticationException(InsufficientAuthenticationException ex) {
    String methodName = "handleInsufficientAuthenticationException()";
    return buildResponseEntity(HttpStatus.UNAUTHORIZED, null, null, ex.getMessage(), null);
}

private ResponseEntity<Object> buildResponseEntity(HttpStatus status, HttpHeaders headers, Integer internalCode, String message, List<Object> errors) {
        ResponseBase response = new ResponseBase()
                .success(false)
                .message(message)
                .resultCode(internalCode != null ? internalCode : status.value())
                .errors(errors != null
                        ? errors.stream().filter(Objects::nonNull).map(Objects::toString).collect(Collectors.toList())
                        : null);
        
        return new ResponseEntity<>((Object) response, headers, status);
    }

SecurityConfig class:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
    
    @Autowired
    private ABAuthenticationEntryPoint authenticationEntryPoint;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
        .....
        .and()
        .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint); //AuthenticationEntryPoint has to be the last
    }
}

Finally you will get something like the following, based on how you buildResponseEntity

{
    "success": false,
    "resultCode": 401,
    "message": "Full authentication is required to access this resource"
}
Aman
  • 1,627
  • 13
  • 19
  • ResponseBase and .resolveException( are not defined – Catalina Mar 18 '21 at 10:35
  • `ResponseBase` is a class that I used as a generic response obj. This is it if it: `public class ResponseBase { private Boolean success; private Integer resultCode; private String message = null; private List errors; private String transactionId; }` – Aman Mar 18 '21 at 11:16
  • i have a last question please, how can i add timestamp to the response ? – Catalina Mar 18 '21 at 11:50
  • and why errors are empty? how can i set them? – Catalina Mar 18 '21 at 11:52
  • 1
    It should be enough to add a field in your response obj and map it inside the `@ControllerAdvice`. For instance, in the above code, you just add it in the `buildResponseEntity` method as `...new ResponseBase() .success(false).timestamp(OffsetDateTime.now(ZoneOffset.UTC))....`, hoping that you use java >= 8 – Aman Mar 18 '21 at 11:54
  • If the errors are empty, I think it should remain empty. Or you can set a custom one in `buildResponseEntity()` – Aman Mar 18 '21 at 11:57
  • Why the result of OffsetDateTime.now is not the same format as `2021-03-18T09:16:09.699+0000` in my example above ? – Catalina Mar 18 '21 at 11:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/230077/discussion-between-aman-and-catalina). – Aman Mar 18 '21 at 11:58
  • I have a question please, if i set the header to "Accept: application/json" then i get that error-response correctly but while sending something like "Accept: text/html" i am getting error : `Failure in @ExceptionHandler .HttpMediaTypeNotAcceptableException: Could not find acceptable representation` – Catalina Mar 19 '21 at 11:01
  • So you have to set the same `Accepts` header also on the controller. – Aman Mar 19 '21 at 11:19
  • works like this: return ResponseEntity.status(internalCode).contentType(PROBLEM).body(response); – Catalina Mar 19 '21 at 11:25
  • I am getting the following error - `org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.web.servlet.HandlerExceptionResolver' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} ` My project has starter-web, starter-cloud-gateway, starter-oauth2-resource-server. Any idea why?? – mifol68042 Jun 16 '22 at 22:35