Spring Rest/Security : body in response is missing then status is 4** [on hold]

53
December 30, 2018, at 9:10 PM

EDITED(SOLVED) : i was using chrome debugger to look network responses, but it was not showing http response. i was writed some tests and now i can see response is correctly returning. thanks everyone :)

I have a spring rest project with using of spring security. I have several endpoints, with configured security and without. When endpoint without security is evaluated response body always included( no matter of status code). But when secured endpoint evaluated, body of response returned only on success. When result code is 4xx response body is missed.

In the example below I tried to return errors in different ways : throwing errors or construct ResposnseEntity with error code. But everywhere when response finished with error code 4xx, body will be missed in response. When returning with HttpStatus.CREATED, body is presented on response.

@PostMapping("/topics")
@PreAuthorize("hasAuthority('KAFKA_EDITOR')")
@ResponseBody
@Transactional
public ResponseEntity addTopic(
        @RequestHeader("teamName") String teamName,
        @RequestBody KafkaTopic topic,
        Authentication authentication
){
    User user = (User)authentication.getPrincipal();
    Team team = teamRepository.findTeamByMembersIsAndTeamName(user,teamName);
    if(team == null) {
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "you did not consist in specified team or this team did not exist");
    }
    long requiredCapacity = team.getCurrentTopicsCount() + topic.getPartitions() * topic.getReplication();
    if(team.getMaxTopicsCount() < requiredCapacity){
        return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(Collections.singletonMap("data", "not acceptable"));
    }
    KafkaTopic findedTopic = kafkaRepository.findByTopicName(topic.getTopicName());
    if(findedTopic != null){
        throw new ResponseStatusException(HttpStatus.CONFLICT,"topic with such name already created");
    }
    topic.setTeam(team);
    topic.setCreator(user);
    kafkaService.createTopic(topic);
    kafkaRepository.save(topic);
    team.setCurrentTopicsCount(team.getCurrentTopicsCount()+1);
    teamRepository.save(team);
    return ResponseEntity.status(HttpStatus.CREATED).body(topic);
}

also I have following security setup :

public class TokenAuthFilter extends AbstractAuthenticationProcessingFilter {
    @Autowired
    private UserRepository repository;
    public TokenAuthFilter(final RequestMatcher requestMatcher){
        super(requestMatcher);
    }
    @Override
    public Authentication attemptAuthentication(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse)
            throws AuthenticationException {
        String token = httpServletRequest.getHeader(AUTHORIZATION);
        if(token == null)
            throw new BadCredentialsException("Missing Auth Token");
        DefaultClaims claims = TokenManager.getClaims(token);
        String username = claims.get("USERNAME").toString();
        Authentication auth = new UsernamePasswordAuthenticationToken(username,username);
        return getAuthenticationManager().authenticate(auth);
    }
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        super.successfulAuthentication(request, response, chain, authResult);
        chain.doFilter(request,response);
    }

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(
        securedEnabled = true,
        prePostEnabled = true
    )
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final RequestMatcher PUBLIS_URLS = new OrRequestMatcher(
            new AntPathRequestMatcher("/auth/**"),
            new AntPathRequestMatcher("/error/**"),
            new AntPathRequestMatcher("/error")
    );
    private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIS_URLS);
    private final TokenAuthProvider provider;
    @Autowired
    public SecurityConfig(TokenAuthProvider provider){
        this.provider = provider;
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(provider);
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().requestMatchers(PUBLIS_URLS);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .exceptionHandling()
            .defaultAuthenticationEntryPointFor(forbiddenEntryPoint(),PROTECTED_URLS)
            .and()
            .authenticationProvider(provider)
            .addFilterBefore(restAuthFilter(), AnonymousAuthenticationFilter.class)
            .authorizeRequests()
            .requestMatchers(PROTECTED_URLS)
            .authenticated()
            .and()
            .csrf().disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable();
    }
    @Bean
    public TokenAuthFilter restAuthFilter() throws Exception {
        TokenAuthFilter filter = new TokenAuthFilter(PROTECTED_URLS);
        filter.setAuthenticationManager(authenticationManager());
        filter.setAuthenticationSuccessHandler(successHandler());
        filter.setAuthenticationFailureHandler(failureHandler());
        return filter;
    }
    @Bean
    public SimpleUrlAuthenticationSuccessHandler successHandler(){
        SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
        successHandler.setRedirectStrategy(new NoRedirectStrategy());
        return successHandler;
    }
    @Bean
    public SimpleUrlAuthenticationFailureHandler failureHandler(){
        SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
        failureHandler.setRedirectStrategy(new NoRedirectStrategy());
        return failureHandler;
    }
    @Bean
    public FilterRegistrationBean disableAutoRegistration(final TokenAuthFilter filter){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }
    @Bean
    public AuthenticationEntryPoint forbiddenEntryPoint(){
        return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN);
    }
}
public class NoRedirectStrategy implements RedirectStrategy {
    @Override
    public void sendRedirect(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s) throws IOException {
    }
}
Answer 1

throwing your exception is not enough, you need to catch them and create your response.

you can use @ExceptionHandler to do that: add this inside your Controller

    @ExceptionHandler(ResponseStatusException.class)
    public ResponseEntity<ApiError> handleException(ResponseStatusException ex) {    
        return new ResponseEntity<>(ex.getMessage(), ex.getStatus());
    }

a more global is to use @ControllerAdvice to handle all your exceptions: here is a good example

READ ALSO
Exception in SOAP Web Service Parser already consumed

Exception in SOAP Web Service Parser already consumed

while executing soap web servicei got exception in org

106
Websocket is not working with WSS in Apache

Websocket is not working with WSS in Apache

Let me explain you the whole scenario in detail:

103
Sum of MYSQL Query

Sum of MYSQL Query

I have a MySQL DB

66
Is it a good idea to register style only in certain WP pages?

Is it a good idea to register style only in certain WP pages?

I have a page speed issue, the theme I bought is really crappy but i cannot change it nowI use WP_rocket, server have HTTP2 but still it is to many resources to load

49