【问题标题】:Header in the response must not be the wildcard '*' when the request's credentials mode is 'include'当请求的凭据模式为“包含”时,响应中的标头不能是通配符“*”
【发布时间】:2026-02-22 17:40:02
【问题描述】:

我使用Auth0 进行用户身份验证,只允许登录用户访问Spring(引导)RestController。此时,我正在创建一个实时消息功能,用户可以使用stompjssockjs 将消息从Angular 2 客户端(localhost:4200)发送到Spring 服务器(localhost:8081)。

在尝试创建 Stomp 客户端并启动连接时,我收到以下控制台错误:

 The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:4200' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

在研究了这个问题之后,似乎无法同时设置选项 origins = * 和 credentials = true。当我已经将 WebSocketConfig 中的允许来源设置为客户端域时,如何解决此问题?

Angular 2 组件

connect() {
    var socket = new SockJS('http://localhost:8081/chat');
    this.stompClient = Stomp.over(socket);  
    this.stompClient.connect({}, function(result) {
        console.log('Connected: ' + result);
        this.stompClient.subscribe('/topic/messages', function(message) {
            console.log(message);
        });
    });
}    

WebSocket配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").setAllowedOrigins("http://localhost:4200").withSockJS();
    }
}

localhost:8081/chat/info?t=1490866768565

{"entropy":-1720701276,"origins":["*:*"],"cookie_needed":true,"websocket":true}

消息控制器

public class MessageController {
    @MessageMapping("/chat")
    @SendTo("/topic/messages")
    public Message send(Message message) throws Exception {
        return new Message(message.getFrom(), message.getText());
    }
}

SecurityConfig(暂时允许所有)

public class SecurityConfig extends Auth0SecurityConfig {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll();
    }
}

更新

经过更多测试和研究,问题似乎只在使用 Chrome 时发生。问题可能与:https://github.com/sockjs/sockjs-node/issues/177

更新

我像提到的 chsdk 一样创建了 CORSFilter,并使用了 addFilterBefore() 方法:https://*.com/a/40300363/4836952

@Bean
CORSFilter corsFilter() {
    CORSFilter filter = new CORSFilter();
    return filter;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(corsFilter(), SessionManagementFilter.class).authorizeRequests().anyRequest().permitAll();
    http.csrf().disable();
}

我可以通过调试看到过滤器被调用,但即使设置了正确的 Access-Control-Allow-Origin,错误消息仍然出现在客户端:

【问题讨论】:

    标签: java spring angular auth0 sockjs


    【解决方案1】:

    问题:

    你没有正确配置'Access-Control-Allow-Origin',你当前的配置被服务器忽略了。

    情况:

    错误堆栈跟踪显示:

    当请求的凭据模式为“包含”时,响应中的'Access-Control-Allow-Origin' 标头的值不能是通配符'*'。 Origin 'http://localhost:4200' 因此不允许访问。

    这意味着除了您不能将'Access-Control-Allow-Origin'设置为通配符"*"之外,您的域'http://localhost:4200'也不允许访问。

    回答你的问题:

    当我已经将 WebSocketConfig 中允许的来源设置为客户端域时,我该如何解决这个问题?

    解决方案:

    我猜你不需要在WebSocketConfig 中设置允许的来源,因为它的目的是按照WebSocket Support in Spring documentation 中的说明在 Web 应用程序中配置 WebSocket 样式的消息传递,你需要在 CORSFilter 配置类中配置它,因为它旨在为 Web 应用程序访问配置 Spring 过滤器。

    这是您在 CORSFilter.java 配置类中需要的:

    public class CORSFilter implements Filter {
    
        // This is to be replaced with a list of domains allowed to access the server
      //You can include more than one origin here
        private final List<String> allowedOrigins = Arrays.asList("http://localhost:4200"); 
    
        public void destroy() {
    
        }
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            // Lets make sure that we are working with HTTP (that is, against HttpServletRequest and HttpServletResponse objects)
            if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
    
                // Access-Control-Allow-Origin
                String origin = request.getHeader("Origin");
                response.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "");
                response.setHeader("Vary", "Origin");
    
                // Access-Control-Max-Age
                response.setHeader("Access-Control-Max-Age", "3600");
    
                // Access-Control-Allow-Credentials
                response.setHeader("Access-Control-Allow-Credentials", "true");
    
                // Access-Control-Allow-Methods
                response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    
                // Access-Control-Allow-Headers
                response.setHeader("Access-Control-Allow-Headers",
                    "Origin, X-Requested-With, Content-Type, Accept, " + "X-CSRF-TOKEN");
            }
    
            chain.doFilter(req, res);
        }
    
        public void init(FilterConfig filterConfig) {
        }
    }
    

    你可以看到:

    private final List<String> allowedOrigins = Arrays.asList("http://localhost:4200");
    

    设置允许访问服务器的域列表。

    参考资料:

    您可能需要查看CORS support in Spring FrameworkEnabling Cross Origin Requests for a RESTful Web Service 以进一步了解它。

    【讨论】:

    • 您能否检查我编辑的问题并告诉我是否忘记了什么?过滤器被调用,但错误消息一直显示在客户端。
    • @Sam 很抱歉,如果我的回复迟到了,我发现您在配置中使用了 http.csrf().disable(),我认为您的情况不需要它..
    • 没问题。我使用了 http.csrf().disable() 因为我的客户端对服务器的请求导致了一个 csrf 错误(不记得细节)。回到家后我会尝试将其删除,如果有效,我会通知您。
    • @pcsantana,已经有一段时间了,但如果我回想一下,我认为这更像是一个 SockJS 错误,所以我选择了一个完整的 StompJS 解决方案。请参阅我的问题下方的评论:github.com/sockjs/sockjs-node/issues/227
    • 好的,我会看到的!谢谢
    【解决方案2】:

    这与您的 spring 或 Angular 应用程序代码无关。

    问题简介
    Access-Control-Allow-Origin 是 CORS(跨域资源共享) 机制的一部分,它为 Web 服务器提供跨域访问控制。它可以保护您的应用/网站免受 CSRF (跨站点请求伪造)

    CORS/CSRF

    问题
    现在,如果我们仔细阅读您的错误

    The value of the 'Access-Control-Allow-Origin' header in the response must 
    not be the wildcard '*' when the request's credentials mode is 'include'. 
    Origin 'http://localhost:4200' is therefore not allowed access.
    

    它说 Access-Control-Allow-Origin 标头不能是通配符。

    换句话说,现在您的后端是说来自整个网络的每个人都可以在我的网站上运行代码。

    我们想要实现的目标:将来源限制为仅您的前端应用 (ng2)。

    解决方案 现在因为您使用的是 Spring,所以我假设您将它与 Apache Tomcat 一起用作您的后端网络服务器。

    CORS 在您的 web.conf(tomcat 文件夹)中定义为过滤器

    找到这一行

    <init-param>
      <param-name>cors.allowed.origins</param-name>
      <param-value>*</param-value>
    </init-param>
    

    并将 * 更改为 http://localhost:4200

    更多关于 Tomcat 中 CORS 配置的信息please read this

    编辑(春季启动)
    因为您使用的是 spring boot,所以您可以将 cors 的配置委托给框架。

    请关注this tutorial on spring.io(如 chsdk 建议),以更好地掌握使用 spring boot 的 CORS 配置。

    【讨论】:

    • 我正在使用 Tomcat / Spring Boot,但找不到 web.conf 文件。我认为由于 Spring Boot,我需要以不同于您的建议的方式配置 Tomcat?
    • @Sam 问题与您的应用程序配置有关,而不是与 Tomcat 配置有关,您应该遵循 Spring 文档中的配置(在我的回答中说明),顺便说一下可以在 application.properties 文件中配置 Tomcat在您的 Spring 启动应用程序中。
    【解决方案3】:

    我的回答为时已晚,但如果有人可能面临同样的问题,我会发布这个,我一直面临着同样的跨域问题。

    基本上,如果您使用在服务器端应用程序上实现的 Spring Security,可能是他阻止了 websocket 握手

    您必须告诉 Spring 安全性允许您的 websocket 端点以允许套接字握手... 使用

    .antMatchers("/socket/**").permitAll()
    

    所以 sockjs 现在可以在切换到 Websocket 协议之前发送一个 GET (Http) 握手请求

    这是 Spring 安全配置

    package org.souhaib.caremy.security.module.config;
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and()
                .authorizeRequests()
                .antMatchers(SecurityParams.PUBLIC_ROUTES).permitAll()
                .antMatchers("/socket/**").permitAll();
    
        http.csrf().disable();
    }}
    

    这是 WebSocket 代理配置

    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            registry.addEndpoint("/socket")
                    .setAllowedOrigins("http://localhost:4200")
                    .withSockJS();
        }
    
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            registry.setApplicationDestinationPrefixes("/app")
                    .enableSimpleBroker("/chat");
        }
    }
    

    【讨论】:

    • 另外,如果您正在运行自定义 CorsConfigurationSource,请不要忘记在那里设置 Orgin
    【解决方案4】:

    只需在 webSocket 配置中添加 .setAllowedOrigins("*")

    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/yourEndpoint");
        stompEndpointRegistry.addEndpoint("/yourEndpoint").setAllowedOrigins("*").withSockJS();
    }
    

    webSocket的版本是1.4.1.RELEASE,如果没有显示方法,你应该更新你的版本。

    【讨论】:

    • 这不适用于这种情况,因为通配符已经设置,但由于“Access-Control-Allow-Origin”并非特定于localhost:4200而出现问题
    最近更新 更多