Заголовок в ответе не должен быть подстановочным знаком "*", если режим учетных данных запроса "включить"
Я использую Auth0
для моей аутентификации пользователей, чтобы разрешить только зарегистрированным пользователям доступ к Spring (загрузка) RestController
, На данный момент я создаю функцию сообщений в режиме реального времени, где пользователи могут отправлять сообщения из Angular 2
клиент (localhost:4200
) к серверу Spring (localhost:8081), используя stompjs
а также sockjs
,
При попытке создать 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.
После исследования этой проблемы кажется, что невозможно установить параметр originins * и 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/ чат / информация т = 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();
}
}
ОБНОВИТЬ
После еще нескольких испытаний и исследований кажется, что проблема возникает только с использованием Chrome. Проблема может быть связана с: https://github.com/sockjs/sockjs-node/issues/177
ОБНОВИТЬ
Я создал CORSFilter, как указано в chsdk, и использовал метод addFilterBefore(): /questions/33459908/spring-boot-security-cors/33459930#33459930.
@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:
5 ответов
Проблема:
Вы не настраиваете 'Access-Control-Allow-Origin'
правильно и ваша текущая конфигурация просто игнорируется сервером.
Ситуация:
Трассировка стека ошибок говорит:
Значение
'Access-Control-Allow-Origin'
заголовок в ответе не должен быть подстановочным знаком'*'
когда режим учетных данных запроса "включить". Источник ' http://localhost:4200/ ', следовательно, не имеет доступа.
Это означает, что помимо того, что вы не можете установить 'Access-Control-Allow-Origin'
подстановочный знак "*"
, ваш домен 'http://localhost:4200'
не разрешен доступ тоже.
Чтобы ответить на ваш вопрос:
Как я могу решить эту проблему, если я уже установил разрешенный источник в WebSocketConfig для клиентского домена?
Решение:
Я думаю, вам не нужно устанавливать допустимое происхождение в WebSocketConfig
поскольку он предназначен для настройки обмена сообщениями в стиле WebSocket в веб-приложениях, как указано в разделе "Поддержка WebSocket в документации Spring", его необходимо настроить в CORSFilter
класс конфигурации, так как он предназначен для настройки Spring Filters для доступа к веб-приложениям.
Это то, что вам нужно в вашем 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 в Spring Framework и Включение запросов перекрестного источника для веб-службы RESTful для дальнейшего изучения этого вопроса.
Это не имеет ничего общего с вашим весенним или угловым кодом приложения.
Введение в вашу проблему
Access-Control-Allow-Origin является частью механизма CORS (Cross-Origin Resource Sharing), который предоставляет веб-серверам контроль междоменного доступа. Он предназначен для защиты вашего приложения / сайта от 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/
для получения дополнительной информации о конфигурации CORS в Tomcat, пожалуйста, прочитайте это
РЕДАКТИРОВАТЬ (весна загрузки)
Поскольку вы используете весеннюю загрузку, вы можете делегировать конфигурацию cors в платформу.
Пожалуйста, следуйте этому руководству на spring.io (как предложено chsdk), чтобы лучше понять конфигурацию CORS с помощью весенней загрузки.
Мой ответ слишком поздно, но я публикую его, если кто-то может столкнуться с той же проблемой, я столкнулся с той же проблемой перекрестного происхождения.
В основном, если вы используете Spring Security, реализованный в вашем серверном приложении, вероятно, именно он блокирует рукопожатие websocket
Вы должны сообщить Spring безопасности, чтобы разрешить конечные точки веб-сокета, чтобы разрешить рукопожатие сокетов... используя
.antMatchers("/socket/**").permitAll()
Таким образом, sockjs теперь сможет отправлять GET (Http) запрос на подтверждение связи перед переключением на протокол Websocket.
Это Spring Security Configuration
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, как показано ниже
и измените метод 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 );
Просто добавь .setAllowedOrigins("*")
в конфиге webSocket.
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/yourEndpoint");
stompEndpointRegistry.addEndpoint("/yourEndpoint").setAllowedOrigins("*").withSockJS();
}
Версия webSocket - 1.4.1. ВЫПУСКИТЕ, вы должны обновить свою версию, если метод не был показан.