說明:本文部份內容均來自慕課網。@慕課網:https://www.imooc.comcss
課程介紹html
什麼是AJAX跨域問題前端
AJAX跨域場景java
AJAX跨域緣由jquery
AJAX跨域問題解決思路nginx
編寫測試代碼web
上面的圖也很清晰了,由於瀏覽器爲了安全(同源),自己就限制了。ajax
值得注意的是:跨域的問題是發生在XMLHttpRequest請求的,也就是說,不是XMLHttpRequest請求是不會有跨域問題的spring
<img src = www.xxxx.xxxx/ >
,URL不是本域的仍是能夠正常獲取該圖片的
代碼編寫apache
1.建立名爲ajax-server的maven工程pom以下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.myimooc</groupId> <artifactId>ajax-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ajax-server</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.編寫AjaxServerStart類
package com.myimooc.ajax.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * <br> * 標題: 啓動類<br> * 描述: AJAX跨域講解後端項目<br> * * @author zc * @date 2018/04/18 */ @SpringBootApplication public class AjaxServerStart { public static void main(String[] args) { SpringApplication.run(AjaxServerStart.class, args); } }
3.編寫ResultBean類
package com.myimooc.ajax.server.vo; import java.io.Serializable; /** * <br> * 標題: REST請求響應POJO類<br> * 描述: 封裝請求響應結果<br> * * @author zc * @date 2018/04/18 */ public class ResultBean implements Serializable{ private static final long serialVersionUID = 7867107433319736719L; private String data; public ResultBean(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } }
4.編寫TestController類
package com.myimooc.ajax.server.controller; import com.myimooc.ajax.server.vo.ResultBean; import com.myimooc.ajax.server.vo.User; import org.springframework.web.bind.annotation.*; /** * <br> * 標題: 測試控制器<br> * 描述: 提供REST服務<br> * 使用 @CrossOrigin 註解支持跨域,能夠放到類或方法上面 * @author zc * @date 2018/04/18 */ @RestController @RequestMapping("/test") //@CrossOrigin public class TestController { @GetMapping("/get1") public ResultBean get1() { System.out.println("TestController.get1"); return new ResultBean("get1ok"); } @PostMapping("/postJson") public ResultBean postJson(@RequestBody User user) { System.out.println("TestController.postJson"); return new ResultBean("postJson" + user.getName()); } @GetMapping("/getCookie") public ResultBean getCookie(@CookieValue(value = "cookie1") String cookie1) { System.out.println("TestController.getCookie"); return new ResultBean("getCookie" + cookie1); } @GetMapping("/getHeader") public ResultBean getHeader( @RequestHeader("x-header1") String header1, @RequestHeader("x-header2") String header2) { System.out.println("TestController.getHeader"); return new ResultBean("getHeader" + header1+header2); } }
代碼編寫
1.建立名爲ajax-client的maven工程pom以下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.myimooc</groupId> <artifactId>ajax-client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ajax-client</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jasmine</artifactId> <version>2.5.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.編寫index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Index</title> <link rel="stylesheet" type="text/css" href="/webjars/jasmine/2.5.0/jasmine.css"> <script src="/webjars/jquery/3.3.0/jquery.min.js"></script> <script src="/webjars/jasmine/2.5.0/jasmine.js"></script> <script src="/webjars/jasmine/2.5.0/jasmine-html.js"></script> <script src="/webjars/jasmine/2.5.0/boot.js"></script> </head> <body> <a href="#" onclick="get1()">發生get1請求</a> <script> function get1() { $.getJSON("http://localhost:8080/test/get1").then( function (res) { console.log(res); } ) } // 每個測試用例的超時時間 jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 請求的接口地址前綴 var base = "http://localhost:8080/test"; // 測試模塊 describe("AJAX講解", function () { // 測試方法 it("get1請求", function (done) { // 服務器返回的結果 var result; $.getJSON(base + "/get1").then( function (res) { result = res; } ); // 因爲是異步請求,須要使用setTimeout來校驗 setTimeout(function () { expect(result).toEqual({ "data":"get1ok" }); // 校驗完成,通知jasmine框架 done(); },100); }); // // 測試方法 // it("jsonp請求", function (done) { // // 服務器返回的結果 // var result; // $.ajax({ // url: base + "/get1", // dataType: "jsonp", // jsonp:"callback2", // success: function (res) { // result = res; // } // }); // // // 因爲是異步請求,須要使用setTimeout來校驗 // setTimeout(function () { // expect(result).toEqual({ // "data":"get1ok" // }); // // // 校驗完成,通知jasmine框架 // done(); // },100); // }); // 測試方法 it("postJson請求", function (done) { // 服務器返回的結果 var result; $.ajax({ url:base+"/postJson", type:"POST", contentType:"application/json;charset=utf-8", data:JSON.stringify({name:"testName"}), success:function(res){ result = res; } }); // 因爲是異步請求,須要使用setTimeout來校驗 setTimeout(function () { expect(result).toEqual({ "data":"postJsontestName" }); // 校驗完成,通知jasmine框架 done(); },100); }); it("getCookie請求", function (done) { // 服務器返回的結果 var result; $.ajax({ url:base+"/getCookie", xhrFields:{ // 發送 AJAX 請求時帶上 cookie withCredentials:true }, success:function(res){ result = res; } }); // 因爲是異步請求,須要使用setTimeout來校驗 setTimeout(function () { expect(result).toEqual({ "data":"getCookietestName" }); // 校驗完成,通知jasmine框架 done(); },100); }); it("getHeader請求", function (done) { // 服務器返回的結果 var result; $.ajax({ url:base+"/getHeader", headers:{ "x-header1":"AAA" }, beforeSend:function(xhr){ xhr.setRequestHeader("x-header2","BBB") }, success:function(res){ result = res; } }); // 因爲是異步請求,須要使用setTimeout來校驗 setTimeout(function () { expect(result).toEqual({ "data":"getHeaderAAABBB" }); // 校驗完成,通知jasmine框架 done(); },100); }); }); </script> </body> </html>
3.編寫application.properties
server.port=8081
4.編寫AjaxClientStart類
package com.myimooc.ajax.client; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AjaxClientStart { public static void main(String[] args) { SpringApplication.run(AjaxClientStart.class, args); } }
5.啓動AjaxServerStart和AjaxClientStart,並訪問http://localhost:8081,點擊發生get1請求,產生跨域問題以下
Chrome瀏覽器的跨域設置
代碼編寫
1.編寫JsonpAdvice類
package com.myimooc.ajax.server.controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; /** * <br> * 標題: JSONP 全局處理<br> * 描述: 統一處理JSONP<br> * * @author zc * @date 2018/04/18 */ @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice{ public JsonpAdvice() { // 與前端約定好回調方法名稱,默認是callback super("callback2"); } }
2.修改index.html
// 測試方法 it("jsonp請求", function (done) { // 服務器返回的結果 var result; $.ajax({ url: base + "/get1", dataType: "jsonp", jsonp:"callback2", success: function (res) { result = res; } }); // 因爲是異步請求,須要使用setTimeout來校驗 setTimeout(function () { expect(result).toEqual({ "data":"get1ok" }); // 校驗完成,通知jasmine框架 done(); },100); });
JSONP的弊端
常見的JavaEE架構
跨域解決方向
被調用方支持跨域
使用Filter解決
編寫代碼
1.編寫CrosFilter類
package com.myimooc.ajax.server.config; import org.springframework.util.StringUtils; import javax.servlet.*; import javax.servlet.FilterConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * <br> * 標題: 服務端解決跨域<br> * 描述: 使用Filter<br> * * @author zc * @date 2018/04/18 */ public class CrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse)response; HttpServletRequest req = (HttpServletRequest)request; // 支持全部域 String origin = req.getHeader("Origin"); if (!StringUtils.isEmpty(origin)){ // 支持任何域名的跨域調用 且 支持帶cookie(是被調用方域名的cookie,而不是調用方的cookie) res.addHeader("Access-Control-Allow-Origin",origin); } // 指定容許的域,帶cookie時,origin必須是全匹配,不能使用 * // res.addHeader("Access-Control-Allow-Origin","http://localhost:8081"); // 容許全部域,但不能知足帶 cookie 的跨域請求 // res.addHeader("Access-Control-Allow-Origin","*"); // 支持全部自定義頭 String headers = req.getHeader("Access-Control-Allow-Headers"); if (!StringUtils.isEmpty(headers)){ // 容許全部header res.addHeader("Access-Control-Allow-Headers",headers); } // 容許全部header // res.addHeader("Access-Control-Allow-Headers","*"); // 指定容許的方法 // res.addHeader("Access-Control-Allow-Methods","GET"); // 容許全部方法 res.addHeader("Access-Control-Allow-Methods","*"); // 容許瀏覽器在一個小時內,緩存跨域訪問信息(即上面三個信息) res.addHeader("Access-Control-Max-Age","3600"); // 啓用 cookie res.addHeader("Access-Control-Allow-Credentials","true"); chain.doFilter(request,response); } @Override public void destroy() { } }
2.編寫FilterConfig類
package com.myimooc.ajax.server.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * <br> * 標題: 配置類<br> * 描述: 註冊CrosFilter<br> * * @author zc * @date 2018/04/18 */ @Configuration public class FilterConfig { @Bean public FilterRegistrationBean registrationBean(){ FilterRegistrationBean filter = new FilterRegistrationBean(); filter.addUrlPatterns("/*"); filter.setFilter(new CrosFilter()); return filter; } }
3.啓動AjaxServerStart和AjaxClientStart,並訪問http://localhost:8081,跨域解決
簡單請求與非簡單請求
Nginx配置
編寫b.com.conf
server{ listen 80; server_name b.com; location /{ proxy_pass http://localhost:8080/; add_header Access-Control-Allow-Methods *; add_header Access-Control-Max-Age 3600; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Headers $http_access_control_allow_headers; if ($request_method = OPTIONS){ return 200; } } }
Apache配置
<VirtualHost *:80> ServerName b.com ErrorLog "logs/b.com-error.log" CustomLog "logs/b.com-access.log" common ProxyPass / http://localhost:8080/ # 把請求頭的origin值返回到Access-Control-Allow-Origin字段 Header always set Access-Control-Allow-Origin "expr=%{req:origin}" # 把請求頭的Access-Control-Allow-Headers值返回到Access-Control-Allow-Headers字段 Header always Access-Control-Allow-Headers "expr=%{Access-Control-Allow-Headers}" Header always set Access-Control-Allow-Methods "*"; Header always set Access-Control-Max-Age "3600"; Header always set Access-Control-Allow-Credentials ""true"; # 處理預檢命令OPTIONS,直接返回204 RewriteEngine On RewriteCond %{REQUEST_METHOD}OPTIONS RewriteRule ^(.*)$"/" [R=204,L] </VirtualHost>
Spring框架支持
使用Nginx反向代理實現
編寫a.com.conf
server{ listen 80; server_name a.com; location /{ proxy_pass http://localhost:8081/; } location /ajaxserver{ proxy_pass http://localhost:8080/test/; } }
使用Apache反向代理實現
<VirtualHost *:80> ServerName a.com ErrorLog "logs/a.com-error.log" CustomLog "logs/a.com-access.log" common ProxyPass / http://localhost:8081/ ProxyPass /ajaxserverapache http://localhost:8080/test </VirtualHost>
課程總結