【问题标题】:Request header field ack is not allowed by Access-Control-Allow-Headers in preflight response even the server already allowing itAccess-Control-Allow-Headers 在预检响应中不允许请求标头字段 ack,即使服务器已经允许它
【发布时间】:2020-07-13 05:57:44
【问题描述】:

我知道这个问题已经遍布整个 stackoverflow,但这个问题完全不同。

尝试使用 axios 访问 java API 时出现错误,方法如下,

axios
      .get("http://127.0.0.1:8090/api/v1/homescreen")
      .then(response => {
        console.log(response);
      })
      .catch(err => {
        console.log(err);
      });
  }

axios 配置是

axios.defaults.headers.common['Content-Type'] = '应用程序/x-www-form-urlencoded'; axios.defaults.headers.common['Authorization'] = 'Bearer eyJhbGciOiJIUzI1N'; axios.defaults.headers.common['Ack'] = 'MTIwNzIwMjBL==' ;

已经尝试使用axios.defaults.headers.common['Content-Type'] = application/json; 并得到同样的错误。

错误是

在“http://127.0.0.1:8090/api/v1/homescreen”访问 XMLHttpRequest 来自原点“http://localhost:8080”已被 CORS 策略阻止: 请求头字段 ack 不允许 预检响应中的 Access-Control-Allow-Headers。

现在在服务器端我已经这样配置了

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(Ordered.LOWEST_PRECEDENCE)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    public SecurityConfig() {
        super();
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable()
            .exceptionHandling().and().authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers(HttpMethod.GET, "/api/v1/cache/**").permitAll()
            .antMatchers("/api/v1/**").authenticated().and().authorizeRequests()
            .and().httpBasic();
    }

    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", 
        "Access-Control-Allow-Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", 
        "Origin", "Cache-Control", "Content-Type", "Authorization", "Ack", "ack", "ackwhatever", "goddamnack"));
        configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    public void configure(final WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS).antMatchers("/api/v1/login/*");
    }

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

即使在弹簧过滤器中,我也放了标题余量

public class HttpRequestAuditFilter implements Filter {

    private static final Logger LOG = LoggerFactory.getLogger("access");
    private static final int MAX_PAYLOAD_LENGTH = 10000;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        if ((request instanceof HttpServletRequest)
            && !(request instanceof ContentCachingRequestWrapper)) {
            request = new ContentCachingRequestWrapper((HttpServletRequest) request);
        }

        HttpServletResponse responseQ = (HttpServletResponse) response;
        HttpServletRequest requestQ = (HttpServletRequest) request;

        try {
            responseQ.setHeader("Access-Control-Allow-Origin", "*");
            responseQ.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            responseQ.setHeader("Access-Control-Max-Age", "3600");
            responseQ.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type, ack, Ack");
            responseQ.setHeader("Access-Control-Expose-Headers", "x-requested-with, authorization, content-type, ack, Ack");

            if ("OPTIONS".equalsIgnoreCase(requestQ.getMethod())) {
                responseQ.setStatus(HttpServletResponse.SC_OK);
            } else {
                chain.doFilter(requestQ, responseQ);
            }

        } finally {
            if (requestQ instanceof HttpServletRequest) {
                performRequestAudit((HttpServletRequest) requestQ);
            }
        }
    }

    public void performRequestAudit(HttpServletRequest httpRequest) {
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(httpRequest, ContentCachingRequestWrapper.class);

        String payload = "";
        if (wrapper != null) {
            byte[] requestBuffer = wrapper.getContentAsByteArray();
            if (requestBuffer.length > 0) {
                int length = Math.min(requestBuffer.length, MAX_PAYLOAD_LENGTH);
                try {
                    payload = new String(requestBuffer,
                        0, length, wrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException unex) {
                    payload = "[Unsupported-Encoding]";
                }
            }
        }
        LOG.trace("{}|{}", payload, wrapper.getHeaderNames());
    }

}

当我尝试使用 curl 时,即使在移动应用程序中,它也能正常工作,只有浏览器出现错误(浏览器本身已经 --disable-web-security)。

任何帮助和解释都将不胜感激。

【问题讨论】:

  • 为什么您的安全配置类的优先级最低?如果您已经配置了弹簧安全性,为什么还要有一个弹簧过滤器来添加标题作为响应。如果您使用 cors 配置 spring security,则不需要过滤器。
  • @Ananthapadmanabhan 看到我正在尽一切努力使这个 axios 工作,所以我的代码有点混乱,但仍然可读
  • 已经尝试过Ordered.HIGHEST_PRECEDENCE 仍然遇到同样的错误
  • 还要在此处与配置类一起提供@WebSecurity注解。

标签: java node.js spring spring-boot axios


【解决方案1】:

与其手动提供 cors 配置类,不如让它成为一个 bean 并让 spring 自动占用它,同时删除最低顺序,以免您的配置被覆盖。还要删除您创建的用于在响应中手动添加标头的 spring 过滤器,因为正确配置时,spring security 将自动添加这些标头作为响应。 @EnableGlobalMethodSecurity 可以与任何 @Configuration 注释类一起使用,但请尝试:

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

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    public SecurityConfig() {
        super();
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
            .exceptionHandling().and().authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers(HttpMethod.GET, "/api/v1/cache/**").permitAll()
            .antMatchers("/api/v1/**").authenticated().and().authorizeRequests()
            .and().httpBasic();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", 
        "Access-Control-Allow-Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", 
        "Origin", "Cache-Control", "Content-Type", "Authorization", "Ack", "ack", "ackwhatever", "goddamnack"));
        configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    public void configure(final WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS).antMatchers("/api/v1/login/*");
    }

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

如果这不起作用,请将方法级别的安全配置分离到另一个配置类,并将网络安全作为单独的配置。

【讨论】:

  • 一样,还是一样的错误..Request header field ack is not allowed by Access-Control-Allow-Headers in preflight response.
  • 您可以尝试将 Spring 安全性和方法级别安全性配置分离到单独的类中,而不是一个。我
  • 看到上面的代码在移动应用、邮递员、甚至 curl 上都能很好地工作。为什么我们需要把它分开?
  • 分离配置可以让您确定配置的哪个方面正在工作以及导致问题的原因,但这不是必需的,由于某种原因,您的 cors 配置没有被 spring 占用。你能检查您的控制台以了解所有标头都添加到 react 的飞行前请求中吗?
  • 嗯实际上这个 cors preflight 问题以前发生在移动应用程序中,在添加 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 现在它在移动设备中工作之后,所以我可以说上面的代码被春天占用了......让我做完整的再次调试
猜你喜欢
  • 2016-05-15
  • 2016-04-24
  • 2019-02-21
  • 2017-12-20
  • 2017-01-30
  • 1970-01-01
  • 2017-12-02
  • 1970-01-01
  • 2016-07-13
相关资源
最近更新 更多