包教不包會系列-跨域
前言
這段時間心態上有點放鬆了,收收心了
昨天晚上無聊,把 SpringBoot 的官方檔案看了一遍,對於一個英語渣來說,真難得
想學的東西很多,但是時間不允許啊。前端 Or 後端,早日決斷吧 ???,太難了
跨域
跨域解決的方案有好幾種,掌握 Cors 就行了,別的瞭解下就可以了
什麼情況下回跨域呢?
跨域是指從一個源去請求另一個源的資源,瀏覽器基於完全考慮並遵循同源策略,禁止跨域訪問。但是我們可以通過一些手段(jsonp 或者 Cors)來實現跨域。
簡單理解:當 url 中的協議/域名/埠 不同時,就產生了跨域。
跨域只會發生在瀏覽器中,後端服務之間的介面呼叫是沒有跨域一說的
跨域的解決方案?
- nginx 反向代理,將請求的介面全部轉發就行了
- jsonp
- cors
nginx 反向代理解決跨域
location /api {
proxy_pass http://192.168.202.50:8082/;
}
匹配 url 中以 /api 開頭的路徑
http://192.168.202.50:8081/api --> http://192.168.202.50:8082
http://192.168.202.50:8081/api/users/2 --> http://192.168.202.50:8082/users/2
複製程式碼
來自 8081 的頁面請求了 /api/users/2 代理介面,nginx 經過路徑匹配,找到對應的 location 處理,將其轉發到實際介面 /8082/users/2,從而規避跨域。
jsonp 解決跨域
jsonp 解決跨域了跨域,但是也有自己的缺陷,服務端需要對跨域和非跨域做不同的處理。
$.ajax({
url:'http:192.168.202.50:8082/users/2'
type:'get',dataType:'jsonp',success(data){},error(error){}
});
複製程式碼
在傳送請求的時候,瀏覽器會多攜帶一個 callback 引數
// 服務端使用 SpringBoot 處理
@GetMapping(value = "/users/2")
public String domainAjaxJsonpNoCookie (HttpServletRequest request){
HashMap<Object,Object> ret = new HashMap<>(16);
ret.put("key1","ajax-jsonp-no-cookie");
ret.put("key2",Math.random());
String callback = request.getParameter("callback");
String retString= JSON.toJSONString(ret);
// 跨域處理,當不是跨域的時候,這樣處理ajax 會拿不到正確的資料
retString=callback+"("+retString+")";
return retString;
}
複製程式碼
Cors
推薦看下 阮一峰-跨域資源共享 CORS 詳解
cors 解決跨域,只需要服務端配合就行,瀏覽器會自動判斷是否跨域,新增 origin 請求頭,服務端只需要新增請求頭 Access-Control-Allow-Origin:* (這樣 cookie 不能攜帶) Access-Control-Allow-Origin:http://192.168.202.8080 (這樣可以攜帶 cookie)
SpringBoot 使用註解 @CrossOrigin
// 註解可以新增到類上或者方法上
@CrossOrigin(value = "http://192.168.202.50:8080",allowCredentials="true")
@GetMapping(value = "/domain/cors/cookie")
public Map domainCorsCookie(HttpServletRequest request,HttpServletResponse response){
Cookie[] cookies = request.getCookies();
HashMap<Object,Object> ret = new HashMap<>(16);
if(cookies!=null){
for (Cookie cookie1 : cookies) {
if (Objects.equals("corsCookie",cookie1.getName())) {
ret.put(cookie1.getName(),cookie1.getValue());
}
}
}
return ret;
}
複製程式碼
CorsFilter 過濾器
@Configuration
public class MyCorsConfig {
@Autowired
private AppProperties appProperties;
/**
* 配置 cors 過濾器
*/
private CorsConfigurationSource getMyCorsConfigurationSource(String path){
CorsConfiguration config = new CorsConfiguration();
// 設定跨域允許攜帶 cookie
config.setAllowCredentials(true);
// 配置允許那些 網址跨域
config.setAllowedOrigins(appProperties.getAllowIp());
// 配置多長時間不用跨域預檢請求
config.setMaxAge(1800L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(path,config);
return source;
}
@Bean
public FilterRegistrationBean registrationCorsFilterBean(){
FilterRegistrationBean filterRegistrationBean =new FilterRegistrationBean();
// 所有的請求都允許跨域
CorsConfigurationSource myCorsConfigurationSource = getMyCorsConfigurationSource("/**");
CorsFilter corsFilter = new CorsFilter(myCorsConfigurationSource);
filterRegistrationBean.setFilter(corsFilter);
return filterRegistrationBean;
}
}
@Configuration
@ConfigurationProperties(prefix = "app.cors")
@Data
public class AppProperties {
private List<String> allowIp;
}
複製程式碼
遇到的坑
- 跨域的時候 cookie 不能攜帶
這個需要客戶端和服務端同時配合,服務端才能拿到 cookie
$.ajax({
url:'http:192.168.202.50:8082/users/2'
type:'get',// 告訴瀏覽器要攜帶 cookie
xhrFields: {
withCredentials: true
},error(error){}
});
複製程式碼
// Access-Control-Allow-Origin:http://192.168.202.8080,
// 這個屬性需要設定對應的地址,設定 * 不行
// allowCredentials 服務端配置允許攜帶 cookie
@CrossOrigin(value = "http://192.168.202.50:8080",allowCredentials="true")
複製程式碼
// 配置過濾器屬性,Access-Control-Allow-Origin 和 allowCredentials
private CorsConfigurationSource getMyCorsConfigurationSource(String path){
CorsConfiguration config = new CorsConfiguration();
// 設定跨域允許攜帶 cookie
config.setAllowCredentials(true);
// 配置允許那些 網址跨域
config.setAllowedOrigins(appProperties.getAllowIp());
// 配置多長時間不用跨域預檢請求
config.setMaxAge(1800L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(path,config);
return source;
}
複製程式碼
cookie Path 設定錯誤
上圖只能看到當前頁面可以訪問的 cookie
檢視域名下所有的 cookie
進入下級頁面即可看到 cookie 選項
可以檢視某個域名下的全部 cookie
Cookie retCookie=new Cookie("serverCookie",String.valueOf(Math.random()));
retCookie.setMaxAge(10000000);
retCookie.setHttpOnly(true);
// 跨域設定 cookie 必須設定 path
// retCookie.setPath("/");
response.addCookie(retCookie);
複製程式碼
當你沒有設定 Path 的時候,預設設定請求介面路徑。
比如說,你登陸介面(/login)返回 cookie(name=login),cookie 沒有設定 path,會自動給你設定 /login。而當你請求/users/2 的介面的時候,是不會攜帶 cookie(name=login)。