Home > Enterprise >  spring AccessDeniedHandler interface don't get called when i have ExceptionHandler of RuntimeEx
spring AccessDeniedHandler interface don't get called when i have ExceptionHandler of RuntimeEx

Time:06-14

I have implemented the AccessDeniedHandler interface and added the interface to the WebSecurityConfigurerAdapter, and I also have an ExceptionHandler of RuntimeException in my ControllerAdvice. When I receive accessDeniedException from MethodSecurity, ExceptionHandler calls RuntimeException before running AccessDeniedHandler.

How to call AccessDeniedHandler without deleting ExceptionHandler?

this is my WebSecurityConfig:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
    public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";

    @Value("${payment.gateway.callback-path}")
    private String paymentCallbackPath;


    @Autowired
    private ThingspodAccessDeniedHandler accessDeniedHandler;

    @Autowired
    private RestAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;


    protected JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        List<String> pathsToSkip = new ArrayList<>(Collections.singletonList(paymentCallbackPath));
        SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter(matcher, JWT_TOKEN_HEADER_PARAM, authenticationFailureHandler);
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(jwtAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().cacheControl().and().frameOptions().disable()
                .and()
                .cors()
                .and()
                .csrf().disable()
                .exceptionHandling()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(paymentCallbackPath).permitAll()
                .and()
                .authorizeRequests()
                .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
                .and()
                .authorizeRequests()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }

}

and this is my AccessDeniedHander and ExceptionHandler in ControllerAdvice:

@Component
public class ThingspodAccessDeniedHandler implements AccessDeniedHandler {

    private final ObjectMapper mapper;

    public ThingspodAccessDeniedHandler(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        Object res = buildResponse(accessDeniedException, "You don't have permission to perform this operation!", HttpStatus.FORBIDDEN, ThingspodErrorCode.PERMISSION_DENIED, request);
        response.setStatus(HttpStatus.FORBIDDEN.value());
        mapper.writeValue(response.getWriter(), res);
    }
}
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Object> handleAllUncaughtRuntimeException(
            RuntimeException ex, WebRequest request){
        return buildApiErrorResponse(ex, HttpStatus.INTERNAL_SERVER_ERROR, ThingspodErrorCode.GENERAL, request);
    }

CodePudding user response:

When selecting a error handler, spring will look for the most specific one.

Since you have exception handler for the more general exception (RuntimeException) and another one for AccessDeniedException.

In that case first exception handler (for handling RuntimeException) won't be called when AccessDeniedException occurs.

So you could keep exception handler for handling RuntimeExceptions, but you will need to add exception handler for handling AccessDeniedException and move logic from ThingspodAccessDeniedHandler into that handler method, like this:

@ExceptionHandler(UnknownHostException.class)
    public void handleUnknownAccessDeniedException(UnknownHostException e, WebRequest request) {
        Object res = buildResponse(accessDeniedException, "You don't have permission to perform this operation!", HttpStatus.FORBIDDEN, ThingspodErrorCode.PERMISSION_DENIED, request);
        response.setStatus(HttpStatus.FORBIDDEN.value());
        mapper.writeValue(response.getWriter(), res);
    }



@ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Object> handleAllUncaughtRuntimeException(
            RuntimeException ex, WebRequest request){
        return buildApiErrorResponse(ex, HttpStatus.INTERNAL_SERVER_ERROR, ThingspodErrorCode.GENERAL, request);
    }
  • Related