当请求的凭据模式为 'include' 时,响应中的 Header 不能是通配符“*”
Header in the response must not be the wildcard '*' when the request's credentials mode is 'include'
我使用 Auth0
进行用户身份验证,只允许登录用户访问 Spring(启动)RestController
。此时,我正在创建一个 real-time 消息功能,用户可以在其中使用 Angular 2
客户端 (localhost:4200
) 将消息发送到 Spring 服务器 (localhost:8081) stompjs
和 sockjs
.
尝试创建 Stomp-client 并启动连接时,我收到以下 console-error:
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();
}
}
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() 方法:.
@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,错误消息仍然出现在客户端:
这与您的 spring 或 angular 应用程序代码无关。
问题介绍
Access-Control-Allow-Origin 是 CORS (Cross-Origin 资源共享) 机制的一部分,它为 Web 服务器提供 cross-domain访问控制。它可以保护您的 app/site 免受 CSRF (Cross-Site 请求伪造).
问题
现在,如果我们仔细阅读您的错误
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 header不能是通配符
换句话说,现在您的 back-end 是说网络上的每个人都可以 运行 在我的网站上编写代码。
我们想要实现的目标:将来源限制为仅您的 front-end 应用程序 (ng2)。
解决方案
现在,因为您正在使用 Spring,我假设您将它与 Apache Tomcat 一起用作您的 back-end 网络服务器。
CORS 在您的 web.conf(tomcat 文件夹)
中被定义为过滤器
找到这一行
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
并将 * 更改为 http://localhost:4200
有关 Tomcat please read this
中 CORS 配置的更多信息
编辑(Spring启动)
因为你使用的是 spring 引导,你可以将 cors 的配置委托给框架。
请遵循 this tutorial on spring.io(如建议的 chsdk)以更好地掌握 spring 启动时的 CORS 配置。
问题:
您没有正确配置 'Access-Control-Allow-Origin'
,您当前的配置只是被服务器忽略。
情况:
错误堆栈跟踪显示:
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'
设置为通配符 "*"
之外,您的域 'http://localhost:4200'
也不允许访问。
回答你的问题:
How can I resolve this when I've already set the allowed origin in the WebSocketConfig to the client domain?
解法:
我想您不需要在 WebSocketConfig
中设置允许的来源,因为它意味着在 Web 应用程序中配置 WebSocket 样式的消息传递 ,如 WebSocket Support in Spring documentation,您需要在 CORSFilter
配置 class 中配置它,因为它意味着配置 Spring Web 应用程序访问过滤器。
这是您在 CORSFilter.java
配置中需要的 class:
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 Framework and Enabling Cross Origin Requests for a RESTful Web Service 以进一步了解它。
只需在 webSocket 配置中添加 .setAllowedOrigins("*")
。
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/yourEndpoint");
stompEndpointRegistry.addEndpoint("/yourEndpoint").setAllowedOrigins("*").withSockJS();
}
webSocket的版本是1.4。1.RELEASE,如果没有显示该方法,你应该更新你的版本。
我的回答为时已晚,但如果有人遇到同样的问题,我会发布这个,我也遇到过同样的 cross-origin 问题。
基本上,如果您使用 Spring 在服务器端应用程序上实施的安全性,可能是他阻止了 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");
}
}
你只需要像下面这样配置你的Cors
并按照我的 class
中给出的 OPTION 方法进行排序
@Component
public class CORSFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin );
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-Dest");
response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
注意这段代码。因为它将重定向来自浏览器的第一个 OPTION 请求
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
在这行配置中您允许凭据
response.setHeader("Access-Control-Allow-Credentials", "true");
从这一行开始,您接受每个来源,它将采用浏览器来源,它不被推荐,但是...
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin );
我使用 Auth0
进行用户身份验证,只允许登录用户访问 Spring(启动)RestController
。此时,我正在创建一个 real-time 消息功能,用户可以在其中使用 Angular 2
客户端 (localhost:4200
) 将消息发送到 Spring 服务器 (localhost:8081) stompjs
和 sockjs
.
尝试创建 Stomp-client 并启动连接时,我收到以下 console-error:
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();
}
}
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() 方法:
@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,错误消息仍然出现在客户端:
这与您的 spring 或 angular 应用程序代码无关。
问题介绍
Access-Control-Allow-Origin 是 CORS (Cross-Origin 资源共享) 机制的一部分,它为 Web 服务器提供 cross-domain访问控制。它可以保护您的 app/site 免受 CSRF (Cross-Site 请求伪造).
问题
现在,如果我们仔细阅读您的错误
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 header不能是通配符
换句话说,现在您的 back-end 是说网络上的每个人都可以 运行 在我的网站上编写代码。
我们想要实现的目标:将来源限制为仅您的 front-end 应用程序 (ng2)。
解决方案 现在,因为您正在使用 Spring,我假设您将它与 Apache Tomcat 一起用作您的 back-end 网络服务器。
CORS 在您的 web.conf(tomcat 文件夹)
中被定义为过滤器找到这一行
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
并将 * 更改为 http://localhost:4200
有关 Tomcat please read this
中 CORS 配置的更多信息编辑(Spring启动)
因为你使用的是 spring 引导,你可以将 cors 的配置委托给框架。
请遵循 this tutorial on spring.io(如建议的 chsdk)以更好地掌握 spring 启动时的 CORS 配置。
问题:
您没有正确配置 'Access-Control-Allow-Origin'
,您当前的配置只是被服务器忽略。
情况:
错误堆栈跟踪显示:
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'
设置为通配符 "*"
之外,您的域 'http://localhost:4200'
也不允许访问。
回答你的问题:
How can I resolve this when I've already set the allowed origin in the WebSocketConfig to the client domain?
解法:
我想您不需要在 WebSocketConfig
中设置允许的来源,因为它意味着在 Web 应用程序中配置 WebSocket 样式的消息传递 ,如 WebSocket Support in Spring documentation,您需要在 CORSFilter
配置 class 中配置它,因为它意味着配置 Spring Web 应用程序访问过滤器。
这是您在 CORSFilter.java
配置中需要的 class:
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 Framework and Enabling Cross Origin Requests for a RESTful Web Service 以进一步了解它。
只需在 webSocket 配置中添加 .setAllowedOrigins("*")
。
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/yourEndpoint");
stompEndpointRegistry.addEndpoint("/yourEndpoint").setAllowedOrigins("*").withSockJS();
}
webSocket的版本是1.4。1.RELEASE,如果没有显示该方法,你应该更新你的版本。
我的回答为时已晚,但如果有人遇到同样的问题,我会发布这个,我也遇到过同样的 cross-origin 问题。
基本上,如果您使用 Spring 在服务器端应用程序上实施的安全性,可能是他阻止了 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");
}
}
你只需要像下面这样配置你的Cors
并按照我的 class
中给出的 OPTION 方法进行排序 @Component
public class CORSFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin );
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-Dest");
response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
}
注意这段代码。因为它将重定向来自浏览器的第一个 OPTION 请求
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(request, response);
}
在这行配置中您允许凭据
response.setHeader("Access-Control-Allow-Credentials", "true");
从这一行开始,您接受每个来源,它将采用浏览器来源,它不被推荐,但是...
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin );