AJAX跨域產(chǎn)生的原因狞洋?
1弯淘、瀏覽器限制
2、XHR(XMLHttpRequest)請求
3吉懊、跨域(域名不同庐橙、端口不同,IP不同)
解決思路
1借嗽、讓瀏覽器不做限制态鳖,不校驗(客戶端改動,意義不大)
2恶导、讓發(fā)出去的請求不是XHR請求(JSONP)
3浆竭、 被調(diào)用方修改代碼,讓其支持http跨域的要求
4 惨寿、調(diào)用方隱藏跨域邦泄,通過代理發(fā)出去的只向A域名的請求只向B域名
具體解決方法
1、通過瀏覽器不做限制
命令行參數(shù)啟動
chrome? --disable-web-security --user-data-dir=D:\Temp
2缤沦、通過JSONP讓發(fā)送出去的請求不是XHR請求
JSON是JSON的一種補充使用方式虎韵,使用JSONP后臺服務(wù)器需要改動
JSONP的原理
1、請求Type不一致缸废,普通請求Type為xhr,jsonp請求類型為script包蓝,瀏覽器只校驗請求類型為xhr的請求,其他類型不校驗
2企量、返回的類型不一樣测萎,普通請求返回的是json對象,jsonp返回的類型的js腳本
JSONP弊端
1届巩、服務(wù)器需要改動代碼進行支持
2硅瞧、只支持GET方法
3、發(fā)送的不是xhr請求恕汇,不支持異步方法
關(guān)鍵代碼:
服務(wù)器端代碼:
Controller
@RestController
@RequestMapping("/test")
public class TestController {
? ? @GetMapping("get1")
? ? public ResultBean get1(){
? ? ? ? System.out.println("TestControllerGet1");
? ? ? ? return new ResultBean("get1 ok");
? ? }
}
切片JsonpAdvice
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
? ? public JsonpAdvice() {
? ? ? ? super("callback");
? ? }
}
客戶端代碼:
$.ajax({
? ? url:base+"/get1",
? ? dataType:"jsonp",
? ? success:function (json) {
? ? ? ? result =json;
? ? }
});
3腕唧、被調(diào)用方解決
跨域請求是直接從瀏覽器發(fā)送到被調(diào)用方服務(wù)器
請求被分為簡單請求和非簡單請求
簡單請求先執(zhí)行后判斷:
方法為:GET、HEAD瘾英、POST
請求header里面:
無自定義頭
Content-Type為一下幾種:
text/plain
multipart/form-data
application/x-www-form-urlencoded
非簡單請求先判斷后執(zhí)行:
方法為:PUT枣接、DELETE的ajax請求
發(fā)送json格式的ajax請求
帶自定義頭的ajax請求
OPTIONS預(yù)檢命令緩存
非簡單請求每次都會發(fā)送兩次請求,影響響應(yīng)效率
res.addHeader("Access-Control-MAX-Age","3600");
表示在一個小時內(nèi)緩存預(yù)檢信息缺谴,不需要在發(fā)送預(yù)檢命令
服務(wù)器實現(xiàn):
被調(diào)用方-Filter上增加過濾
通過對比Origin進行判斷是否跨域
res.addHeader("Access-Control-Allow-Origin","*");
res.addHeader("Access-Control-Allow-Methods","*");
res.addHeader("Access-Control-Allow-Headers","*");
帶Cookie的跨域
客戶端ajax請求帶Cookie但惶,只需要在ajax請求中加入
xhrFields:{
withCredentials:true
}
在帶有Cookie的請求中:
必須啟動cookie,res.addHeader("Access-Control-Allow-Credentials","true");
不能給res.addHeader("Access-Control-Allow-Origin","*");賦值*,必須指定請求的Origin
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)){
? ? res.addHeader("Access-Control-Allow-Origin",origin);
}
帶自定義頭的跨域
不能給res.addHeader("Access-Control-Allow-Headers","*");寫死
String headers = req.getHeader("Access-Control-Allow-Headers");
if (!StringUtils.isEmpty(headers)){
? ? res.addHeader("Access-Control-Allow-Headers",origin);
}
關(guān)鍵代碼:
DemoApplication啟動類中加入Filter過濾器
@Bean
public FilterRegistrationBean registerFilter(){
? ? FilterRegistrationBean bean = new FilterRegistrationBean();
? ? bean.addUrlPatterns("/*");
? ? bean.setFilter(new CrosFilter());
? ? return bean;
}
實現(xiàn)CrosFilter類
public class CrosFilter implements javax.servlet.Filter {
? ? @Override
? ? public void init(FilterConfig filterConfig) throws ServletException {}
? ? @Override
? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
? ? ? ? HttpServletResponse res = (HttpServletResponse)servletResponse;
? ? ? ? HttpServletRequest req = (HttpServletRequest) servletRequest;
? ? ? ? String origin = req.getHeader("Origin");
? ? ? ? if (!StringUtils.isEmpty(origin)){
? ? ? ? ? ? res.addHeader("Access-Control-Allow-Origin",origin);
? ? ? ? }
? ? ? ? res.addHeader("Access-Control-Allow-Origin","*");
? ? ? ? res.addHeader("Access-Control-Allow-Methods","*");
? ? ? ? res.addHeader("Access-Control-Allow-Headers","*");
? ? ? ? res.addHeader("Access-Control-MAX-Age","3600");
? ? ? ? res.addHeader("Access-Control-Allow-Credentials","true");
? ? ? ? filterChain.doFilter(servletRequest,res);
? ? }
? ? @Override
? ? public void destroy() {}
}
客戶端代碼:
$.ajax({
type : "post",
url: base + "/postJson",
contentType : "application/json;charset=utf-8",
data: JSON.stringify({name: "xiaofengqing"}),
success: function(json){
result = json;
}
});
被調(diào)用方-Nginx配置
nginx增加虛擬主機:在nginx.conf 最后添加 include vhost/*.conf;
新建vhost目錄在目錄中新建一個配置文件
客戶端調(diào)用
將客戶端ajax請求指向nginx服務(wù)器即可膀曾,如上:b.com
被調(diào)用方-apache配置
打開apache.conf配置文件
開啟虛擬主機配置:vhost_alias_module
打開虛擬主機配置文件:#Virtual hosts
開啟proxy代理模塊:proxy_module
開啟proxy_http代理模塊:proxy_http_module
開啟Header模塊:headers_module
開啟rewrite模塊:rewrite_module
打開虛擬主機的配置文件vhost.conf
客戶端調(diào)用
將客戶端ajax請求指向nginx服務(wù)器即可县爬,如上:b.com
被調(diào)用方-spring框架解決方案
增加Controller中@CrossOrigin注解即可
@SpringBootApplication
@CrossOrigin
public class DemoApplication {
? ? public static void main(String[] args) {
? ? ? ? SpringApplication.run(DemoApplication.class, args);
? ? }
}
4、調(diào)用方解決
基于隱藏跨域請求添谊,跨域請求不會直接發(fā)送到被調(diào)用發(fā)服務(wù)器财喳,而是通過nginx/apache服務(wù)器轉(zhuǎn)發(fā)過去(在無法修改服務(wù)器端代碼的情況下使用)
調(diào)用方解決-NGINX
調(diào)用方解決-APACHE