一、什么是跨域问题
是两个项目之间使用ajax(前端类似与后端技术httpclient)实现通讯,如果浏览器访问的域名地址和端口号与ajax访问的地址和端口号不一致的情况下,默认情况下浏览器会有安全机制,这个机制就是跨域问题,会造成无法获取到返回结果(但实际还是可以访问的,请求状态码为200,但无法获取到结果)。
二、跨域问题怎么解决:
跨域问题有如下5中解决方案:
1、使用jsonp解决 跨域问题(不推荐,因为只能支持get请求,不支持post请求)
2、使用httpclient进行转发(不推荐,因为效率非常低,会发送两次请求)
3、设置响应头允许跨域(可以推荐)适合于小公司快速解决问题
4、使用Nginx搭建API接口网关(强烈推荐)因为保证域名和端口都一致,以项目区分反向代理到真实服务器地址。
5、使用Zuul微服务搭建API接口网关(强烈推荐)SpringCloud
网站跨域问题演示:
新建两个maven项目(xwhy_web_a,xwhy_web_b),pom类型为war类型。
pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<!-- SpringBoot 对lombok 支持 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- SpringBoot web 核心组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<!-- SpringBoot 外部tomcat支持 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- springboot-log4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<!-- springboot-aop 技术 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
application.yml (a项目端口号为8080,b项目端口号为8081)
server:
port: 8080
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
在b项目中新建BIndexController.java类
@RestController
@SpringBootApplication
public class BIndexController {
// 该接口提供给A项目进行ajax调用
@RequestMapping("/getBInfo")
public Map<String, Object> getBInfo() {
Map<String, Object> result = new HashMap<String, Object>();
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
return result;
}
public static void main(String[] args) {
SpringApplication.run(BIndexController.class, args);
}
}
在a项目中新建AIndexController.java类
@Controller
@SpringBootApplication
public class AIndexController {
@RequestMapping("/aIndexJsp")
public String aIndexJsp() {
return "aIndexJsp";
}
public static void main(String[] args) {
SpringApplication.run(AIndexController.class, args);
}
}
在a项目中新建aIndexJsp.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript"
src="http://www.itmayiedu.com/static/common/jquery-1.7.2.min.js?t=2017-07-27"></script>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "POST",
async : false,
url : "http://a.itmayiedu.com:8081/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
</head>
<body>我是A项目,正在调用B项目
</body>
</html>
在hosts文件最下面加上如下代码:
127.0.0.1 a.itmayiedu.com
127.0.0.1 b.itmayiedu.com
启动两个项目,访问如下地址:a.itmayiedu.com:8080/aIndexJsp,这时候就会可以打开f12可以看到浏览器控制台已经报错了,调用getBInfo调用状态码200,但是没有返回值,这就是跨域问题导致的。
解决方案:
1、设置响应头允许跨域
这种一般都在小项目里面使用,大项目不会使用这种方式
在BIndexController里面进行修改
// 该接口提供给A项目进行ajax调用
@RequestMapping("/getBInfo")
public Map<String, Object> getBInfo(HttpServletResponse response) {
// 告诉客户端(浏览器 )允许跨域访问 *表示所有域名都是可以 在公司中正常的代码应该放入在过滤器中
response.setHeader("Access-Control-Allow-Origin", "*");
Map<String, Object> result = new HashMap<String, Object>();
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
return result;
}
2、使用jsonp方式
原理:使用script发送get请求,将一个参数传过去,然后回调的时候在带回来进行解析.
将aIndexJsp的ajax请求参数进行修改:
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "POST",
async : false,
url : "http://a.itmayiedu.com:8081/getBInfo",
dataType : "jsonp",
jsonp : "jsonpCallback",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
BIndexController进行修改:
//使用jsonp 解决跨域问题
@RequestMapping("/getBInfo")
public void getBInfo(HttpServletResponse response, String jsonpCallback) throws IOException {
JSONObject result = new JSONObject();
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
PrintWriter writer = response.getWriter();
writer.println(jsonpCallback + "(" + result.toJSONString() + ")");
writer.close();
}
3、使用httpclient内部转发
优点:安全,因此真实接口调用地址
缺点:两次请求效率低下,代码重复
实现步骤,
1.ajax请求地址还是请求本项目的地址
2.在本项目中我们接受该请求
3.在后端我们使用httpclient进行转发
4.转发之后我们在将获取到请求通过response返回到前端页面
在AIndexController.java类中增加接口:
// 使用HttpClient进行方法B接口
@RequestMapping("/forWardB")
@ResponseBody
public JSONObject forWardB() {
JSONObject result = HttpClientUtils.httpGet("http://b.itmayiedu.com:8081/getBInfo");
return result;
}
在ajax中修改请求地址:
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "POST",
async : false,
url : "http://a.itmayiedu.com:8080/forWardB",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
4、使用nginx搭建API接口网关
nginx.conf文件配置
server {
listen 80;
### 拦截域名地址
server_name api.itmayiedu.com;
### 拦截以/a开头的请求
location /a {
### 指定上游服务器负载均衡服务器
proxy_pass http://a.itmayiedu.com:8080;
index index.html index.htm;
}
location /b {
### 指定上游服务器负载均衡服务器
proxy_pass http://b.itmayiedu.com:8081;
index index.html index.htm;
}
}
ajax访问地址修改:
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "POST",
async : false,
url : "http://api.itmayiedu.com/b/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
5、使用Zuul搭建API接口网关