AJAX 크로스 도메인
참고: 이 기사의 내용 중 일부는 MOOC.com에서 가져온 것입니다. @MUOC.COM: https://www.imooc.com
강의 소개
●AJAX 크로스 도메인 문제란
●생성 AJAX 크로스 도메인 문제의 원인
●AJAX 크로스 도메인 문제를 해결하는 사고와 방법
AJAX 크로스 도메인 문제란
● 쉽게 말하면 프론트엔드가 백엔드 서비스 인터페이스를 호출하는 경우입니다
●서비스 인터페이스가 동일한 도메인에 있지 않으면 크로스 도메인 문제가 발생합니다
#🎜🎜 #AJAX 크로스 도메인 시나리오#🎜🎜 # ●프런트엔드와 백엔드 분리 및 서비스 중심 개발 모델
●프론트엔드와 프런트엔드 개발이 독립적 , 프런트엔드는 많은 수의 백엔드 인터페이스를 호출해야 합니다
●백엔드 인터페이스가 동일한 도메인이 아닌 한 도메인 간 문제가 발생합니다
●교차 도메인 문제는 매우 일반적이며, 교차 도메인 문제를 해결하는 것도 중요합니다.
AJAX 교차 도메인 이유
●브라우저 제한 사항: 브라우저 보안 확인 제한
●교차 도메인(프로토콜, 도메인 이름, 포트의 차이는 교차 도메인으로 간주됨)
●XHR(XMLHttpRequest )요청#🎜🎜 #
AJAX 교차 도메인 문제 해결 아이디어 ●브라우저: 브라우저는 실제 가치가 거의 없는 교차 도메인 확인을 제거합니다 ● XHR: 하지 마세요 XHR을 사용하고, 단점이 많고 현재 개발 요구 사항을 충족할 수 없는 JSONP를 사용하세요 ●도메인 간: 호출 수신자 수정은 도메인 간 호출을 지원합니다(지정된 매개 변수). 호출자 수정은 도메인 간 호출을 숨깁니다. (프록시 기준)테스트 코드 작성● Callee 백엔드 코드 작성: Spring Boot● 호출자 프론트 엔드 코드 작성: Jquery ●프론트 엔드 Jasmine 테스트 프레임워크 소개 교차 도메인 문제는 왜 발생하나요?위 그림도 매우 명확합니다. 브라우저 자체에 보안 제한이 있기 때문입니다(동일 소스).
●XMLHttpRequest 요청을 보낼 때 요청한 도메인(호스트 도메인 이름, 포트)이 다르면 도메인 간 문제가 발생합니다(클라이언트가 서버에서 반환한 데이터를 얻을 수 없음). XMLHttpRequest 요청에서 도메인 간 문제가 발생한다는 점은 주목할 가치가 있습니다. 즉, XMLHttpRequest 요청이 아닌 경우 도메인 간 문제가 발생하지 않습니다 ●예를 들어 매우 간단한 예: 웹페이지 를 작성할 때 URL이 이 도메인#🎜에 없어도 이미지를 정상적으로 얻을 수 있습니다. 🎜#
교차 도메인 문제 해결을 고려 중
환경 구축#🎜🎜 #
2- 1 백엔드 프로젝트
코드 작성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); } }
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); } }
2-2 프런트엔드 프로젝트코드 작성
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>
<!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. application.properties
server.port=8081
4를 작성합니다. #
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); } }
크로스 도메인 해결 방법3-1 검사 금지#🎜 🎜#
Chrome 브라우저 크로스 도메인 설정● Windows 방법
참고 문서: https://www.cnblogs .com/laden...
사용 지침: 추가: --disable-web -security --user-data-dir=C:MyChromeDevUserData#🎜🎜 속성 페이지의 대상 입력 상자에 #●
Mac OS 메서드참고문서 :
http://blog.csdn.net/justinji... 使用说明:用命令行打开 Google Chrome:open -a "Google Chrome" --args --disable-web-security 3-2 使用JSONP 代码编写 1.编写JsonpAdvice类 2.修改index.html JSONP的弊端 服务器需要改动代码支持只支持GET发送的不是XHR请求 3-3 支持跨域 常见的JavaEE架构 跨域解决方向 ●被调用方解决 ●基于支持跨域的解决思路 ●基于Http协议关于跨域的相关规定,在响应头里增加指定的字段告诉浏览器,允许调用 ●跨域请求是直接从浏览器发送到被调用方 ●修改被调用方的Http服务器 调用方解决 ●基于隐藏跨域的解决思路 ●跨域请求不会浏览器直接发送到被调用方 ●而是从中间的Http服务器(Apache、Nginx)转发过去 ●修改调用方的Http服务器 被调用方支持跨域 ●【重点】Web应用服务器(Tomcat、Netty、WebLogic或应用程序)实现 ●Http服务器(Nginx)配置实现 ●Http服务器(Apache)配置实现 使用Filter解决 编写代码 1.编写CrosFilter类 2.编写FilterConfig类 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 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在最后面增加下面的内容即可 Spring框架支持 在类或方法上使用注解@CrossOrigin即可支持跨域 3-4 隐藏跨域 ●使用Nginx反向代理实现 ●修改主机hosts文件增加映射本地域名:127.0.0.1 a.com ●在vhost目录下创建a.com.conf ●启动niginx,访问a.com/ajaxserver/get1 编写a.com.conf 使用Apache反向代理实现 修改conf/extra/httpd-vhosts.conf在最后面增加下面的内容即可 课程总结 4-1 课程总结 课程总结 ●产生原因:主要是浏览器对Ajax请求的限制 ●解决思路:JSONP、支持跨域、隐藏跨域 ●核心原理:了解Http协议关于跨域方面的规定 ●해결책: 필터 사용, Nginx 정방향 및 역방향 프록시, Apache 정방향 및 역방향 프록시, Spring 프레임워크 지원 위 내용은 Ajax 크로스 도메인에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!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");
}
}
// 测试方法
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);
});
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() {
}
}
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;
}
}
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;
}
}
}
<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>
server{
listen 80;
server_name a.com;
location /{
proxy_pass http://localhost:8081/;
}
location /ajaxserver{
proxy_pass http://localhost:8080/test/;
}
}
<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>