由於瀏覽器連接的是服務器上的websocket,導致出現瞭如下跨域錯誤:
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://a.com' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute。
先分析下出現這個問題的原因:由於前後端分離導致我們通常會寫跨域配置(如下代碼)。但是報錯很明顯指出request's credentials mode is 'include',那麼響應就the response must not be the wildcard '*’。所以直接配置Access-Control-Allow-Origin的值爲通配是行不通的。具體怎麼解決我接下來會講。
package cn.datainvest.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 解決跨越配置類
* @author yangfeng
*
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
/**
* 跨域過濾器
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}
springboot集成websocket步驟如下:
1.引入依賴
<!--Websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2:WebSocketConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @author yangfeng
**/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
}
}
3.業務代碼
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class Weather {
@Resource
private IWeatherService weatherService;
private static Logger LOG = LoggerFactory.getLogger(WeatherScheduler.class);
@Autowired
private SimpMessagingTemplate messagingTemplate;
/**
* 保存天氣信息
*/
public void saveWeather() {
ServiceResult<WeatherAO> serviceResult = weatherService.getWeatherAndSave();
if (serviceResult != null && serviceResult.isSucceed() && serviceResult.getData() != null) {
WeatherAO weather = serviceResult.getData();
//發送數據到webscoket
messagingTemplate.convertAndSend("/topic/weather", weather);
}
}
}
4.vue連接socket,開發環境訪問地址:localhost:8089。很明顯連接這裏出現跨域了。
<template>
<div class="">、
<h1>Stock Price</h1>
<div>
<button @click="senMessage">發送消息</button>
<ul>
<li v-for="m in list1" :key="m.name">{{ m.name }}: {{ m.price }}</li>
</ul>
</div>
</div>
</template>
<script>
import SockJS from 'sockjs-client'
import Stomp from 'webstomp-client'
export default {
data() {
return {
list1: [],
stompClient: null
}
},
mounted() {
this.initWebSocket()
},
methods: {
senMessage() {
this.stompClient.send('/app/hello')
},
initWebSocket() {
this.connection()
// 需要有一個失敗重連得到問題
},
connection() {
// 更換that指針
const socket = new SockJS('http://112.174.126.131:8080/websocket')
this.stompClient = Stomp.over(socket)
//建立連接,訂閱主題
this.stompClient.connect({}, (frame) => {
console.log(frame)
this.stompClient.subscribe('/topic/terminalTrace', (val) => {
// this.list1 = JSON.parse(val.body)
console.log('-------++++++++++++++++++++++++++++++------------')
//下面會報錯,應該是json的問題,但是數據可以接收到
console.log(val.body)
})
this.stompClient.subscribe('/topic/weather', (val) => {
// this.list1 = JSON.parse(val.body)
console.log('-------++++++++++++++++++++++++++++++------------')
//下面會報錯,應該是json的問題,但是數據可以接收到
console.log(val.body)
})
})
// 回調函數 3 end
}
}
}
</script>
5.使用filter來解決
把之前寫的跨域類CorsConfig 刪除,加入下面的filter。
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Component
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:8089");
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, PUT, 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) {
}
}
加入origin的路徑配置:
6.測試