0

I have a org.springframework.web.filter.GenericFilterBean filter. I would like to throw an exception when user is not authorized and catch this exception with @ControllerAdvice. But it seems that the handler doesn't do that.

Filter method

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        ClientAuthentication auth = (ClientAuthentication) authenticationService.getAuthentication(httpRequest, httpResponse);
        SecurityContextHolder.getContext().setAuthentication(auth);

        if (auth.isAuthenticated()) {
            chain.doFilter(request, response);
        } else {
            ObjectMapper mapper = new ObjectMapper();
            response.setCharacterEncoding("UTF-8");
            httpResponse.getWriter().write(mapper.writeValueAsString(auth.getInfo()));
        }
    }

It works but a disadvantage is that I want to catch exception and render exception message back to the client with respect to Content-Type and Accept HTTP header. This solution renders what I want but into JSON only and my application has to handle XML and JSON.

Exception handler

it works when I throw exceptions from @Controller or @RestController but not from HTTP filter.

    @ControllerAdvice
    class GlobalExceptionHandler {

        private static final Logger LOGGER = LogManager.getLogger(GlobalExceptionHandler.class);

        @ExceptionHandler(BadRequestException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public
        @ResponseBody
        ResponseMessage badRequestException(BadRequestException ex) {
            return ex.getResponseMessage();
        }

/** rest omitted .... **/

    }

Update #1

This is my Spring Security config where AuthFilter filter is set.

@EnableWebSecurity
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Configuration
    @Order(1)
    public static class ApiSecurity extends WebSecurityConfigurerAdapter {

        private static Logger LOG = LogManager.getLogger(ApiSecurity.class);

        @Bean
        AuthenticationEntryPoint authenticationEntryPoint() {
            LOG.debug("authenticationEntryPoint bean");
            return new RestAuthenticationEntryPoint();
        }

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

            http.csrf().disable();

            http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
                    .and()
                    .servletApi()
                    .and()
                    .headers().cacheControl();

            http.antMatcher(ApiController.API_ROOT + "/**").authorizeRequests().anyRequest().authenticated()
                    .and()
                    .addFilterBefore(new AuthFilter(), UsernamePasswordAuthenticationFilter.class);

        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers(ApiController.API_ROOT + "/sandbox/**");
        }

    }

    @Configuration
    public static class WebFormSecurity extends WebSecurityConfigurerAdapter {

        @Autowired
        private UserDetailsService defaultUserService;

        @Autowired
        private PasswordEncoder passwordEncoder;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(defaultUserService).passwordEncoder(passwordEncoder);
        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/public/**");
        }

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

            http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
                    .loginPage("/login").failureUrl("/login?error").permitAll().and()
                    .logout().permitAll();


        }
    }

}
jnemecz
  • 3,171
  • 8
  • 41
  • 77
  • `Filter`s execute before the mechanism that calls the exception handlers, so it will simply be not possible to make it work like you want. But why are you throwing exceptions yourself? Seems like you are trying to hack around Spring Security... Use the frameworks don't work around them. – M. Deinum Dec 12 '16 at 11:05
  • 1
    As fas as I know `@ControllerAdvice` works at spring dispatcher level and not at filter level. I have been thinking about trying to `@Autowire` the mvc message converters into the filter just to manage what you are seeking, but I still have not found a clean way of doing it... – jlumietu Dec 12 '16 at 11:08
  • @M.Deinum - why I throw an exception? I had a plan to throw an exception and catch it in @ControllerAdvice and render it into XML / JSON. What should I do to render response to the client into JSON / XML in `Filter`? – jnemecz Dec 12 '16 at 11:21
  • As mentioned you are trying to work around Spring Security, instead of working around it, work with it. Spring Security already does all those checks, if you ned a custom way of obtaining the `Authentication` implement a custom `AuthenticationProvider` or `UserDetailsService`. The Spring Security exception handling will do the rest. You can extend that to return what you want it to return. – M. Deinum Dec 12 '16 at 11:41
  • I've added my Spring Security config, do I work around SS? Is there way how to use my AuthFilter and work with SS? I'll try to look at `AuthenticationProvider`, `UserDetailService` I use for authentication in web based part of my app. Please see update. – jnemecz Dec 12 '16 at 14:25
  • 1
    Possible duplicate of [How to manage exceptions thrown in filters in Spring?](http://stackoverflow.com/questions/34595605/how-to-manage-exceptions-thrown-in-filters-in-spring) – holmis83 Dec 12 '16 at 20:17

0 Answers0