對于前后端分離的項目來說,如果前端項目與后端項目部署在兩個不同的域下,那么勢必會引起跨域問題的出現。
什么是跨域?
跨域,指的是瀏覽器不能執(zhí)行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制。簡單來說就是不同域名之間相互訪問
同源策略:請求的url地址,必須與瀏覽器上的url地址處于同域上,也就是域名,端口,協議相同.
例如:
http://www.a.com 要訪問 http://www.b.com 這樣域名不同,屬于跨域
http://www.a.com:8081/index.html調用http://www.a.com:8080的接口 這樣端口號不同 也屬于跨域
http://www.a.com:訪問https://www.a.com 這樣也是屬于跨域 協議不一樣
所以在以前前后端不分離的開發(fā)模式中,從來不用關注這個問題,因為所有的請求都是在同一個域內
綜合了網上解決跨域的方式目前有兩種
1.使用JSONP
不過我不太推薦使用,JSONP只支持GET請求,不管是對于前端還是后端來說,寫法與我們平常的ajax寫法不同,同樣后端也需要作出相應的更改。
JSONP的優(yōu)勢在于支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。
2.通過cors協議解決跨域問題
CORS支持所有類型的HTTP請求。
這個也是我非常推薦大家使用的方法
下面我來說下項目中遇到的問題
前端:vue.js
后端:springboot
前端請求接口時,報錯信息如下
Origin 'http://localhost:8080' is therefore not allowed access.
The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute
解決方法:
前端請求開啟 withCredentials: true
后臺接口也需要對應的將 Access-Control-Allow-Credentials 設為 true。
前端攜帶token傳后端時報錯
Request header field token is not allowed by
Access-Control-Allow-Headers in preflight response.
這是由于請求頭內并沒有這個名叫token的請求頭字段
默認情況下,只有六種 simple response headers (簡單響應首部)可以暴露給外部:
Cache-Control Content-Language Content-Type Expires Last-Modified Pragma
如果想要讓客戶端可以訪問到其他的首部信息,可以將它們在
Access-Control-Expose-Headers 里面列出來。
所以解決方法是:
在Access-Control-Allow-Headers添加自定義的請求頭字段Token
如果還有其他請求頭字段需要使用,也是需要在Access-Control-Allow-Headers添加,不然服務器是不允許的
后面還有一個問題:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access.
The response had HTTP status code 503.
這個是請求頭返回服務不可用
解決方式:
我看了下代碼原來是我寫了兩個跨域過濾一個是用寫過濾器Filter,一個是繼承
webmvcconfigureradapter然后我注釋了其中一個就可以了,我想應該是兩個跨域處理器沖突了
拓展
現在開發(fā)客戶端都是發(fā)非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者
Content-Type字段的類型是application/json。也就是通過json數據交互
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
流程
瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發(fā)出正式的
XMLHttpRequest請求,否則就報錯。
"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是Origin,表示請求來自哪個源。
一般不特殊指定源的話都將Access-Control-Allow-Origin設置為*
除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法
所使用到的方法也是要在跨域處理器中說明
也就是在Access-Control-Allow-Methods中加入各種請求方法
例如:POST PUT, GET, OPTIONS, DELETE
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發(fā)送的頭信息字段
這個上面有說到,項目需要什么額外的請求頭字段就添加上去
服務器收到"預檢"請求以后,檢查了Origin、Access-Control-Request-Method和
Access-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。
說了這么
我分享下我的跨域處理的代碼(我這里是使用過濾器的寫法,也可以使用繼承
webmvcconfigureradapter的方法)
@Component
public class CorsFilter implements Filter {
/*跨域請求配置*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest reqs = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "5000");
response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,Authorization,Token");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}
繼承webmvcconfigureradapter的方法
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers")
.exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
.allowCredentials(true).maxAge(3600);
}
}
注:以上寫法二選一即可,兩個都寫估計還是會沖突,兩者作用一樣只是寫法不同