当请求的凭据模式为“include”时,响应中的标头不能是通配符“*”
我正在使用Auth0
进行用户身份validation,只允许登录用户访问Spring(Boot) RestController
。 此时我正在创建一个实时消息function,用户可以使用stompjs
和sockjs
将消息从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); }); }); }
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}
MessageController
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(); } }
UPDATE
经过一些更多的测试和研究后,似乎问题只发生在Chrome上。 问题可能与以下内容有关: https : //github.com/sockjs/sockjs-node/issues/177
UPDATE
我创建了像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(); }
我可以看到通过调试它来调用Filter,但即使设置了正确的Access-Control-Allow-Origin,错误消息仍会出现在客户端上:
问题:
您没有正确配置'Access-Control-Allow-Origin'
,服务器会忽略您当前的配置。
情况:
错误堆栈跟踪说:
当请求的凭据模式为“include”时,响应中
'Access-Control-Allow-Origin'
标头的值不能是通配符'*'
”。 因此不允许来源’ http:// localhost:4200 ‘访问。
这意味着除了您不能将'Access-Control-Allow-Origin'
设置为通配符"*"
,您的域'http://localhost:4200'
也不允许访问。
回答你的问题:
当我已经将WebSocketConfig中允许的原点设置为客户端域时,如何解决此问题?
解:
我猜你不需要在WebSocketConfig
设置允许的原点,因为它意味着在Web应用程序中配置WebSocket样式的消息传递,如Spring文档中的WebSocket支持中所述,您需要在CORSFilter
配置类中配置它,因为它意味着配置Spring Filters以进行Web应用程序访问。
这是您在CORSFilter.java
配置类中需要的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 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 allowedOrigins = Arrays.asList("http://localhost:4200");
设置允许访问服务器的域列表。
参考文献:
您可能需要查看Spring Framework中的CORS支持以及为RESTful Web服务启用跨源请求以进一步阅读它。
这与您的弹簧或角度应用程序代码无关。
介绍你的问题
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一起用作后端Web服务器。
CORS在web.conf(tomcat文件夹)中被定义为filter
找到这一行
cors.allowed.origins *
并将*更改为http:// localhost:4200
有关Tomcat中CORS配置的更多信息, 请阅读此内容
编辑 (春季启动)
因为您使用的是spring boot,所以可以将cors的配置委派给框架。
请按照spring.io 上的本教程 (如chsdk建议),通过spring boot更好地掌握CORS配置。
我的答案为时已晚,但如果有人遇到同样的问题,我会发布这个问题,我一直面临同样的跨领域问题。
基本上,如果您使用在服务器端应用程序上实现的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 Broker配置
@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"); } }
只需在webSocket配置中添加.setAllowedOrigins("*")
即可。
@Override public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) { stompEndpointRegistry.addEndpoint("/yourEndpoint"); stompEndpointRegistry.addEndpoint("/yourEndpoint").setAllowedOrigins("*").withSockJS(); }
webSocket的版本是1.4.1.RELEASE,如果没有显示方法,你应该更新你的版本。