AJAX domänenübergreifend
Hinweis: Ein Teil des Inhalts dieses Artikels stammt von MOOC.com. @MOOC.com: https://www.imooc.com
Kurseinführung
●Was ist das AJAX-Cross-Domain-Problem?
●Die Gründe für AJAX-Cross-Domain Problem
●Gedanken und Methoden zur Lösung von domänenübergreifenden AJAX-Problemen
Was ist ein domänenübergreifendes AJAX-Problem?
●Einfach ausgedrückt ist es, wenn das Front-End das aufruft Back-End-Dienstschnittstelle
●Wenn sich die Dienstschnittstelle nicht in derselben Domäne befindet, treten domänenübergreifende Probleme auf
AJAX-domänenübergreifendes Szenario
●Ein Entwicklungsmodell der Front-End- und Back-End-Trennung und der serviceorientierten Entwicklung
● Die Front-End- und Back-End-Entwicklung sind unabhängig und das Front-End muss eine große Anzahl von Back-End-Schnittstellen aufrufen
●Solange sich die Back-End-Schnittstelle nicht in derselben Domäne befindet, treten domänenübergreifende Probleme auf
●Domänenübergreifende Probleme sind sehr häufig und müssen gelöst werden. Domänenübergreifende Probleme sind auch sehr wichtig
AJAX-domänenübergreifende Gründe
●Browsereinschränkungen: Einschränkungen bei der Überprüfung der Browsersicherheit
●Domänenübergreifend (jedes Protokoll, jeder Domänenname, jeder Port) Alle Unterschiede wird als domänenübergreifend betrachtet)
● , von geringem tatsächlichen Wert
●XHR: Verwenden Sie nicht XHR, sondern JSONP, das viele Nachteile hat und die aktuellen Entwicklungsanforderungen nicht erfüllen kann
●Domänenübergreifend: Der Angerufene wird so geändert, dass er domänenübergreifende Aufrufe unterstützt (Parameter angeben); der Aufrufer ändert versteckte domänenübergreifende Aufrufe (basierend auf dem Proxy)
Testcode schreiben
● Callee-Back-End-Code schreiben: Spring Boot
●Caller-Front-End-Code schreiben: Jquery
●Einführung des Front-End-Jasmine-Testframeworks
Warum treten domänenübergreifende Probleme auf?
Das obige Bild ist auch sehr klar, da der Browser selbst Einschränkungen aus Sicherheitsgründen hat (gleicher Ursprung). ●Wenn wir eine XMLHttpRequest-Anfrage senden und die angeforderte Domäne (Hostdomänenname, Port) unterschiedlich ist, treten domänenübergreifende Probleme auf (der Client kann die vom Server zurückgegebenen Daten nicht abrufen)
Es ist erwähnenswert, dass bei XMLHttpRequest-Anfragen domänenübergreifende Probleme auftreten. Das heißt, es treten keine domänenübergreifenden Probleme auf, wenn es sich nicht um eine XMLHttpRequest-Anfrage handelt.
●Ein sehr einfaches Beispiel: Wann Schreiben einer Webseite, , wenn sich die URL nicht in der lokalen Domäne befindet, kann das Bild trotzdem normal abgerufen werden
Ideen für Lösung domänenübergreifender Probleme
Umgebungseinrichtung
2-1 Backend-Projekt
Code-Schreiben
1. Erstellen Sie ein Maven-Projekt mit dem Namen ajax-server wie folgt:<?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. Schreiben Sie die AjaxServerStart-Klasse
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. Schreiben Sie eine ResultBean-Klasse
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. Schreiben Sie eine TestController-Klasse
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); } }
2-2 Front-End-ProjektCode schreiben
1. Erstellen Sie ein Maven-Projekt mit dem Namen ajax-client wie folgt:<?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. Schreiben Sie index.html
<!DOCTYPE html> <html> <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. Schreiben Sie die Anwendung .properties
server.port=8081
Schreiben Sie die AjaxClientStart-Klasse
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); } }
Starten Sie AjaxServerStart und AjaxClientStart und besuchen Sie http://localhost :8081, klicken Sie, um eine Get1-Anfrage zu generieren, was wie folgt zu domänenübergreifenden Problemen führt
Domänenübergreifend lösen3-1 Überprüfung verbieten
Domänenübergreifende Einstellungen des Chrome-Browsers
●
Windows-MethodeReferenzdokument: https://www.cnblogs.com/laden... Gebrauchsanweisung: Zieleingabe auf der Eigenschaftenseite. Im Feld hinzufügen: --disable-web-security --user-data-dir=C:MyChromeDevUserData
●
Mac OS-MethodeReferenzdokument: http://blog.csdn.net/justinji...
使用说明:用命令行打开 Google Chrome:open -a "Google Chrome" --args --disable-web-security
3-2 使用JSONP
代码编写
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的弊端
服务器需要改动代码支持只支持GET发送的不是XHR请求
3-3 支持跨域
常见的JavaEE架构
跨域解决方向
●被调用方解决
●基于支持跨域的解决思路
●基于Http协议关于跨域的相关规定,在响应头里增加指定的字段告诉浏览器,允许调用
●跨域请求是直接从浏览器发送到被调用方
●修改被调用方的Http服务器
调用方解决
●基于隐藏跨域的解决思路
●跨域请求不会浏览器直接发送到被调用方
●而是从中间的Http服务器(Apache、Nginx)转发过去
●修改调用方的Http服务器
被调用方支持跨域
●【重点】Web应用服务器(Tomcat、Netty、WebLogic或应用程序)实现
●Http服务器(Nginx)配置实现
●Http服务器(Apache)配置实现
使用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,跨域解决
简单请求与非简单请求
●简单请求:浏览器先发送真正的请求后检查
●请求方法:GET、HEAD、POST的一种
●请求header:无自定义header;Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded的一种
●非简单请求:浏览器先发预检命令,检查通过后,才发送真正的请求
●常见的有:PUT、DELETE
●其它条件:发送Json格式的请求、带自定义header的请求
●预检命令:浏览器检测到跨域请求, 会自动发出一个OPTIONS请求, 就是所谓的预检(preflight)请求。当预检请求通过的时候,才发送真正的请求。
Nginx配置
●修改主机hosts文件增加映射本地域名:127.0.0.1 b.com(表示被调用方的域名)
●在conf目录下创建vhost目录
●修改nginx.conf在最后面增加一行代码:include vhost/*.conf;
●在vhost目录下创建b.com.conf
●启动niginx,访问b.com/test/get1
编写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配置
●修改conf/httpd.conf找到LoadModule vhost_alias_module module/mod_vhost_alias.so取消注释
●修改conf/httpd.conf找到LoadModule proxy_module module/mod_ proxy.so取消注释
●修改conf/httpd.conf找到LoadModule proxy_http_module module/mod_ proxy_http.so取消注释
●修改conf/httpd.conf找到LoadModule headers_module module/mod_ headers.so取消注释
●修改conf/httpd.conf找到LoadModule rewrite_module module/mod_ rewrite.so取消注释
●修改conf/httpd.conf找到Include conf/extra/httpd-vhosts.conf取消注释
●修改conf/extra/httpd-vhosts.conf在最后面增加下面的内容即可
<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框架支持
在类或方法上使用注解@CrossOrigin即可支持跨域
3-4 隐藏跨域
●使用Nginx反向代理实现
●修改主机hosts文件增加映射本地域名:127.0.0.1 a.com
●在vhost目录下创建a.com.conf
●启动niginx,访问a.com/ajaxserver/get1
编写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反向代理实现
修改conf/extra/httpd-vhosts.conf在最后面增加下面的内容即可
<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>
课程总结
4-1 课程总结
课程总结
●产生原因:主要是浏览器对Ajax请求的限制
●解决思路:JSONP、支持跨域、隐藏跨域
●核心原理:了解Http协议关于跨域方面的规定
●Lösung: Verwenden Sie Filter, Nginx-Forward- und Reverse-Proxys, Apache-Forward- und Reverse-Proxys, Spring-Framework-Unterstützung
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Ajax Cross-Domain. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!