Home > Back-end >  How to map 403 error to user friendly error?
How to map 403 error to user friendly error?

Time:02-22

I'm new to building rest APi using spring boot.

Here is my controller snippet

@PreAuthorize("hasRole('ADMIN')")
    @PostMapping(value = "/api/post/posts", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PostDto> createPost(@Valid @RequestBody PostDto postDto) {

        System.out.println("postDto : "    postDto.getId());
        return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);

    }

this is my Security config

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)    //give method level security
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/api/**").permitAll().anyRequest()
                .authenticated().and().httpBasic();
    }

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        // In Memory Users
        UserDetails ashish = User.builder().username("oxana").password(getPasswordEncoder().encode("password")).roles("USER").build();
        UserDetails admin = User.builder().username("admin").password(getPasswordEncoder().encode("admin")).roles("ADMIN").build();

        return new InMemoryUserDetailsManager(ashish, admin);
    }
    
    @Bean
    PasswordEncoder getPasswordEncoder() {
        
        return new BCryptPasswordEncoder();
    }
}

I'm trying land above exception here

@ExceptionHandler(Exception.class)
    public ResponseEntity<Errors> handleGlobalException(Exception exception,
                                                               WebRequest webRequest){
        Error errorDetails = new Error();
        errorDetails.setErrorDesc(exception.getMessage());
        errorDetails.setErrorCode(Error.ErrorCodeEnum.BAD_REQUEST);
        Errors errors = new Errors();
        errors.addErrorsItem(errorDetails);
        
        return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
    }

but its not coming and giving a big mess of error, like this

"timestamp": "2022-02-21T11:39:28.797 00:00",
    "status": 403,
    "error": "Forbidden",
    "trace": "org.springframework.security.access.AccessDeniedException: Access is denied\r\n\tat org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)\r\n\tat org.springframework.security.access.intercept.aopalliance.

Can anyone please suggest me, How can I handle or catch this exception to customize error, where user has no access to do something ?

Thanks

Update

Implemented AccessDeniedHandler in below way

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Dont have sufficient priviliges to perform this action")
public class AccessDeniedError implements AccessDeniedHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exec)
            throws IOException, ServletException {

        response.sendRedirect("Dont have sufficient priviliges to perform this action");

    }

}

And now able to get message like this

{
    "timestamp": "2022-02-21T13:29:08.377 00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/api/post/Dont have sufficient priviliges to perform this action"
}

Its somewhat better, but how can I take control of these variables ("error", "message", "status") values from above response, so that I could add mine custom values in it ?

CodePudding user response:

The AccessDeniedException is handled by the ExceptionTranslationFilter which then delegates to the AccessDeniedHandler to write to corresponding response to the client.

If you want to customize this behavior, then you can implement a AccessDeniedHandler and then set your implementation to the HttpSecurity object.

MyAccessDeniedHandler.java

public class MyAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        writeCustomResponse(response);
    }

    private void writeCustomResponse(HttpServletResponse response) {
        if (!response.isCommitted()) {
            try {
                response.getWriter().write("{ \"error\": \"User is not authorized.\"}");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

SecurityConfiguration.java

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // set the customized AccessDeniedHandler to the HttpSecurity object
        http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
    }
}
  • Related