Request not reaching to the controller but still get 200 response

两盒软妹~` 提交于 2020-06-09 02:54:28

问题


I was playing around spring security and trying to secure an restful application, but then ran into this rather absurd problem. All the action on my controllers are fine and the requests are accepted but the request actually never reaches the controller and always 200 is returned with no content.

My security config looks likes so:


package com.bpawan.interview.api.config;

import com.bpawan.interview.api.model.Error;
import com.bpawan.interview.api.security.JWTAuthenticationFilter;
import com.bpawan.interview.api.security.JWTAuthorizationFilter;
import com.bpawan.interview.service.UserDetailService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import static com.bpawan.interview.api.security.SecurityConstants.LOGIN_URL;
import static com.bpawan.interview.api.security.SecurityConstants.SIGN_UP_URL;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailService userDetailService;

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

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler())
                .authenticationEntryPoint(authenticationEntryPoint())
                .and()
                .addFilterBefore(corsFilter(), SessionManagementFilter.class)
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
                .antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()
                .antMatchers(
                        "/v2/api-docs",
                        "/configuration/ui",
                        "/swagger-resources/**",
                        "/configuration/security",
                        "/swagger-ui.html",
                        "/webjars/**",
                        "/actuator/**"
                ).permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:8080");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);

        return new CorsFilter(source);
    }

    private AuthenticationEntryPoint authenticationEntryPoint() {
        return (httpServletRequest, httpServletResponse, e) -> {
            var error = Error
                    .builder()
                    .message("Not authenticated")
                    .status(401)
                    .build();

            var responseBody = new ObjectMapper().writeValueAsString(error);

            httpServletResponse.setContentType(MediaType.APPLICATION_JSON.toString());
            httpServletResponse.getWriter().append(responseBody);
            httpServletResponse.setStatus(401);
        };
    }

    private AccessDeniedHandler accessDeniedHandler() {
        return (httpServletRequest, httpServletResponse, e) -> {
            var error = Error
                    .builder()
                    .message("Access denied")
                    .status(403)
                    .build();

            var responseBody = new ObjectMapper().writeValueAsString(error);

            httpServletResponse.getWriter().append(responseBody);
            httpServletResponse.setStatus(403);
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON.toString());
        };
    }
}

The controller looks like so:

package com.bpawan.interview.api.controller;

import com.bpawan.interview.dal.entity.Candidate;
import com.bpawan.interview.dal.repository.CandidateRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.util.List;

@RestController
@RequestMapping("api/candidate")
@RequiredArgsConstructor
@Slf4j
public class CandidateController {
    private final CandidateRepository candidateRepository;

    @GetMapping
    public List<Candidate> getAll(Principal principal) {

        log.info(principal.toString());

        return this.candidateRepository.findAll();
    }

    @GetMapping("/{candidateId}")
    public Candidate getById(@PathVariable Long candidateId) {
        return this.candidateRepository
                .findById(candidateId)
                .orElseThrow(() -> new RuntimeException("Could not find the candidate for the provided id."));
    }

    @PostMapping
    public Candidate addCandidate(@RequestBody Candidate candidate) {
        return this.candidateRepository.save(candidate);
    }

    @DeleteMapping("/{candidateId}")
    public void deleteCandidate(@PathVariable Long candidateId) {
        this.candidateRepository.deleteById(candidateId);
    }
}

The Authorization filter looks like so:

package com.bpawan.interview.api.security;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

import static com.bpawan.interview.api.security.SecurityConstants.HEADER_STRING;
import static com.bpawan.interview.api.security.SecurityConstants.TOKEN_PREFIX;

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain
    ) throws IOException, ServletException {
        final var header = request.getHeader(HEADER_STRING);

        if (null != header) {
            final var headerContainsPrefix = header.startsWith(TOKEN_PREFIX);

            if (!headerContainsPrefix) {
                chain.doFilter(request, response);
                return;
            }
            return;
        }


        UsernamePasswordAuthenticationToken authentication = this.getAuthentication(request);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        final var token = request.getHeader(HEADER_STRING);

        if (null != token) {
            final var user = JWT
                    .require(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes()))
                    .build().verify(token.replace(TOKEN_PREFIX, ""))
                    .getSubject();

            if (null != user) {
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }

            return null;
        }
        return null;
    }
}

I can make a login request and get the valid Bearer token and seems to work with the authentication against the database.

All the actuator endpoints works fine too also the swagger endpoints. Only the controllers that I have written do not. Even when I put the breakpoints on the controllers to see if the it actually goes in there but nope.

I am feeling like I might have a silly mistake somewhere. Any help would be greatly appreciated.

Here is the sample log of the request that I did using logback-access:

logging uri: GET /api/candidate HTTP/1.1 | status code: 200 | bytes: 0 | elapsed time: 1 | request-log:  | response-log: 

But cannot see the trace on the actuator's endpoint actuator/httptrace whatsoever as if the request ever happened.


回答1:


After struggling some time I found the problem. It was indeed a stupid mistake that I made. and it was on the Authorization filter. changed

 if (null != header) {
            final var headerContainsPrefix = header.startsWith(TOKEN_PREFIX);

            if (!headerContainsPrefix) {
                chain.doFilter(request, response);
                return;
            }
            return;
        }

to :

 if (null != header) {
            final var headerContainsPrefix = header.startsWith(TOKEN_PREFIX);

            if (!headerContainsPrefix) {
                chain.doFilter(request, response);
                return;
            }
        }

and seems to solve the problem.



来源:https://stackoverflow.com/questions/56722709/request-not-reaching-to-the-controller-but-still-get-200-response

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!