瀏覽器只允許請求當前域的資源,而對其他域的資源表示不信任。那怎麼纔算跨域呢?
- 請求協議
http,https
的不同 - 域
domain
的不同 - 端口
port
的不同
好好好,大概就是這麼回事啦,下面我們講2種中規中矩的辦法:CORS
,JSONP
document.domain,window.name,web sockets就先別鬧了,腰不好 : )
2、CORS
這是W3C的標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。我們先來看看整個流程
在此之前,需要知道簡單請求
複雜請求
這兩個小朋友
1.簡單請求:
1): 請求方式只能是:head
,get
,post
2): 請求頭允許的字段:Accept
,Accept-Language
,Content-Language
,Last-Event-ID
Content-Type
:application/x-www-form-urlencoded、multipart/form-data、text/plain 三選一
2.複雜請求:除了簡單請求的其他情況
請求頭origin字段爲當前域
服務器:誒,你是誰,我來看看你的origin,嗯嗯,可以,符合我的要求,放行!順便告訴你,老夫的規矩!
其中,最重要的就是Access-Control-Allow-Origin
,標識允許哪個域的請求。當然,如果服務器不通過,根本沒有這個字段,接着觸發XHR
的onerror
,再接着你就看到瀏覽器的提示xxx的服務器沒有響應Access-Control-Allow-Origin字段
//指定允許其他域名訪問
'Access-Control-Allow-Origin:http://172.20.0.206'//一般用法(*,指定域,動態設置),3是因爲*不允許攜帶認證頭和cookies
//是否允許後續請求攜帶認證信息(cookies),該值只能是true,否則不返回
'Access-Control-Allow-Credentials:true'
上面第一行說到的Access-Control-Allow-Origin
有多種設置方法:
- 設置
*
是最簡單粗暴的,但是服務器出於安全考慮,肯定不會這麼幹,而且,如果是*的話,遊覽器將不會發送cookies
,即使你的XHR
設置了withCredentials
- 指定域,如上圖中的
http://172.20.0.206
,一般的系統中間都有一個nginx
,所以推薦這種 - 動態設置爲請求域,多人協作時,多個前端對接一個後臺,這樣很方便
withCredentials
:表示XHR
是否接收cookies和發送cookies,也就是說如果該值是false,響應頭的Set-Cookie
,瀏覽器也不會理,並且即使有目標站點的cookies,瀏覽器也不會發送。
複雜請求:
最常見的情況,當我們使用put
和delete
請求時,瀏覽器會先發送option
(預檢)請求,不過有時候,你會發現並沒有,這是後面我們會講到緩存。
預檢請求
與簡單請求不同的是,option請求多了2個字段:Access-Control-Request-Method
:該次請求的請求方式Access-Control-Request-Headers
:該次請求的自定義請求頭字段
服務器檢查通過後,做出響應:
//指定允許其他域名訪問
'Access-Control-Allow-Origin:http://172.20.0.206'//一般用法(*,指定域,動態設置),3是因爲*不允許攜帶認證頭和cookies
//是否允許後續請求攜帶認證信息(cookies),該值只能是true,否則不返回
'Access-Control-Allow-Credentials:true'
//預檢結果緩存時間,也就是上面說到的緩存啦
'Access-Control-Max-Age: 1800'
//允許的請求類型
'Access-Control-Allow-Methods:GET,POST,PUT,POST'
//允許的請求頭字段
'Access-Control-Allow-Headers:x-requested-with,content-type'
這裏有個注意點:Access-Control-Request-Method
,Access-Control-Request-Headers
返回的是滿足服務器要求的所有請求方式,請求頭,不限於該次請求.
問題所在:
1. 避免將Access-Control-Allow-Origin設置爲null和*
2. 禁止直接反射HTTP請求頭中的Origin字段值。對請求頭Origin值做嚴格過濾、校驗。具體來說,可以使用“全等於”判斷,或使用嚴格的正則(如:^https://domain\.qq\.com$)進行判斷。
設置 Access-Control-Allow-Origin 有多種方式,這裏寫兩種
一. 微服務中整合springSecurity,
1.編寫過濾器
package com.wf.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@Slf4j
public class CORSFilter implements Filter {
@Value("${allow.origin.regex}") //apollo配置 ,允許跨域訪問的主體
private String originRegex;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", originRegex);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
public void destroy() {}
}
2.添加過濾器
package com.wf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity security) throws Exception {
security.csrf().disable();
security.headers().frameOptions().disable();
//加入過濾器
security.addFilterBefore(new CORSFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
2. nginx 配置
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "X-Requested-With";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
}
如果同時配置服務器過濾器與nginx ,則會看到頁面錯誤:
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed
看上面錯誤提示,contains multiple values "*" 意思就是設置了2次跨域,但是隻有一個是允許的,移除其中的任意一個就好了。如果服務器設置了允許跨域,使用Nginx代理裏面就不需要了(或者就不用使用Nginx了)