当请求的凭据模式为“include”

时间:2017-03-30 10:09:22

标签: java spring angular auth0 sockjs

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

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

sockjs

在研究此问题后,看起来无法同时设置选项origins = *和credentials = true。当我已将WebSocketConfig中允许的原点设置为客户端域时,如何解决此问题?

Angular 2组件

 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.

WebSocketConfig

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

本地主机:8081 /聊天/信息T = 1490866768565

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

MessageController

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

SecurityConfig(暂时允许所有)

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

更新

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

有关

更新

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

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

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

enter image description here

4 个答案:

答案 0 :(得分:15)

<强>问题:

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

<强>情况:

错误堆栈跟踪说:

  

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

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

回答你的问题:

  

如果我已将WebSocketConfig中允许的原点设置为客户端域,我该如何解决这个问题?

<强>解决方案:

我想您不需要在WebSocketConfig中设置允许的来源,因为它意味着按照WebSocket Support in Spring documentation中的说明在Web应用程序中配置 WebSocket样式的消息,您需要在CORSFilter配置类中对其进行配置,因为它旨在为Web应用程序访问配置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 support in Spring FrameworkEnabling Cross Origin Requests for a RESTful Web Service以便进一步阅读。

答案 1 :(得分:6)

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

问题简介
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 please read this

中CORS配置的更多信息

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

请按照this tutorial on spring.io(如chsdk建议的那样)通过spring boot更好地掌握CORS配置。

答案 2 :(得分:2)

我的答案为时已晚,但如果有人遇到同样的问题,我会发布这个问题,我一直面临同样的跨性别问题。

基本上如果你使用服务器端应用程序上实现的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");
    }
}

答案 3 :(得分:-2)

只需在webSocket config添加.setAllowedOrigins("*")

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

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