请求未到达控制器,但仍收到200响应 [英] Request not reaching to the controller but still get 200 response

查看:134
本文介绍了请求未到达控制器,但仍收到200响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究Spring安全性,并试图保护一个宁静的应用程序,但是后来遇到了这个相当荒谬的问题.我的控制器上的所有操作都很好,请求已被接受,但请求实际上从未到达控制器,并且始终返回200,但不包含任何内容.

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());
        };
    }
}

控制器看起来像这样:

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);
    }
}

授权过滤器如下所示:

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;
    }
}

我可以发出登录请求并获得有效的Bearer令牌,并且似乎可以对数据库进行身份验证.

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.

这是我使用logback-access进行的请求的示例日志:

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: 

但是无法看到执行器端点上的轨迹actuator/httptrace,就像请求曾经发生过一样.

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

推荐答案

经过一段时间的努力,我发现了问题所在.我确实犯了一个愚蠢的错误.它在授权过滤器上.更改

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;
        }

至:

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

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

似乎可以解决问题.

这篇关于请求未到达控制器,但仍收到200响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆