当请求的凭据模式为“包含”时,响应中的标头不得为通配符“*”

2022-09-01 01:08:43

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

尝试创建 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 中将允许的源设置为客户端域时,如何解决此问题?

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

WebSocketConfig

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

本地主机:8081/聊天/信息 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());
    }
}

安全配置(暂时允许所有)

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://stackoverflow.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();
}

我可以看到过滤器是通过调试它来调用的,但是即使设置了正确的访问控制-允许-源,错误消息也会不断出现在客户端:

enter image description here


答案 1

问题:

您配置不正确,服务器会忽略当前配置。'Access-Control-Allow-Origin'

情况:

错误堆栈跟踪显示:

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

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

要回答您的问题:

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

溶液:

我想你不需要在 中设置允许的源,因为它意味着在 Web 应用程序中配置 WebSocket 样式的消息传递,如 Spring 文档中的 WebSocket 支持所述,你需要在配置类中配置它,因为它意味着配置 Spring Filters 以进行 Web 应用程序访问。WebSocketConfigCORSFilter

这是您在配置类中需要的: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");

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

引用:

您可能需要查看Spring Framework中的CORS支持启用RESTful Web服务的跨域请求,以进一步阅读有关它的信息。


答案 2

这与你的弹簧或角度应用程序代码无关。

问题
简介 访问控制-允许-源是 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.

它说访问控制-允许-源标头不能是通配符。

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

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

溶液现在,因为您正在使用Spring,我将假设您正在将其与Apache Tomcat一起使用作为后端Web服务器。

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

找到此行

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

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

有关Tomcat中CORS配置的更多信息,请阅读此内容

EDIT ( Spring boot )
由于您使用的是 Spring boot,因此您可以将 cors 的配置委托给框架。

请按照本教程 spring.io(如chsdk建议的那样)更好地掌握弹簧靴的CORS配置。