1.什么是网站跨域
跨域原因产生:在当前域名请求网站中,默认不允许通过ajax请求发送其他域名。
两个项目之间使用ajax(前端类似后端的httpclient)实现通讯,如果浏览器访问的域名和端口与ajax请求访问的地址不一样的情况下,默认情况下浏览器会有安全机制,这个机制跨域问题,会无法获取返回结果。
原理图:
2.环境准备
2.1 配置hosts的域名
127.0.0.1 a.learn.com
127.0.0.1 b.learn.com
2.2 跨域问题复现
A项目接口
@RequestMapping("/aIndexJsp")
public String aIndexJsp() {
return "aIndexJsp";
}
A项目jsp
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://b.learn.com:8081/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
B项目接口
@RequestMapping("/getBInfo")
public Map<String, Object> getBInfo(HttpServletResponse response) {
Map<String, Object> result = new HashMap<String, Object>(10);
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
return result;
}
2.3跨域问题
**说明:**跨域问题只是没办法拿到响应,跨域问题只存在前端,但是ajax地址拿到浏览器中是可以拿到响应的。
3.五种网站跨域解决方案
3.1.使用jsonp解决网站跨域
A前端代码
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://b.learn.com:8081/getBInfo",
dataType : "jsonp",
//服务端用于接收callback调用的function名的参数
jsonp : "jsonpCallback",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
B项目接口
/**
* 该接口提供给A项目基于jsonp进行ajax调用
* @param response response
* @return map
*/
@RequestMapping(value = "/getBInfo", method = RequestMethod.GET)
public void ajaxB(HttpServletResponse response, String jsonpCallback) throws IOException {
JSONObject root = new JSONObject();
root.put("retCode", "200");
root.put("errorMsg", "登陆成功");
response.setHeader("Content-type", "text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.print(jsonpCallback + "(" + root.toString() + ")");
writer.close();
}
效果演示:
请求: jsonp传递随机数
响应:响应的时候返回随机数
缺点:不支持post请求,代码书写比较复杂
3. 2.使用HttpClient内部转发
A前端代码
<!-- 基于httpclient请求 -->
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://a.learn.com:8080/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
A接口
// 使用HttpClient进行方法B接口
@RequestMapping("/getBInfo")
@ResponseBody
public JSONObject forWardB() {
JSONObject result = HttpClientUtils.httpGet("http://b.learn.com:8081/getBInfo");
return result;
}
B项目接口
@RequestMapping("/getBInfo")
public Map<String, Object> getBInfo(HttpServletResponse response) {
Map<String, Object> result = new HashMap<String, Object>(10);
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
return result;
}
效果演示:
缺点:内部转发,相当于两次请求
3. 3.使用设置响应头允许跨域
A前端代码
<!-- 正常请求 -->
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://b.learn.com:8081/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
B项目
/**
* 该接口提供给A项目设置请求头进行ajax调用
*
* @param response response
* @return map
*/
@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;
}
3. 4.基于Nginx搭建企业级API接口网关
nginx配置:
server {
listen 80;
server_name api.learn.com;
#charset utf-8;
#charset koi8-r;
#access_log logs/host.access.log main;
location /a/ {
proxy_pass http://a.learn.com:8080/;
###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
#proxy_connect_timeout 1s;
###nginx发送给上游服务器(真实访问的服务器)超时时间
#proxy_send_timeout 1s;
### nginx接受上游服务器(真实访问的服务器)超时时间
#proxy_read_timeout 1s;
#root data/www;
index index.html index.htm;
}
location /b/ {
proxy_pass http://b.learn.com:8081/;
###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
#proxy_connect_timeout 1s;
###nginx发送给上游服务器(真实访问的服务器)超时时间
#proxy_send_timeout 1s;
### nginx接受上游服务器(真实访问的服务器)超时时间
#proxy_read_timeout 1s;
#root data/www;
index index.html index.htm;
}
A前端代码:
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://api.learn.com/b/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
B项目:
/**
* 该接口提供给A项目正常进行ajax调用
*
* @param response response
* @return map
*/
@RequestMapping("/getBInfo")
public Map<String, Object> getBInfo(HttpServletResponse response) {
Map<String, Object> result = new HashMap<String, Object>(10);
result.put("retCode", "200");
result.put("retMsg", "登陆成功");
return result;
}
/a/ 和/b/以项目区分,利用nginx反向代理
效果演示:
3.5.使用Zuul搭建微服务API接口网关
注册中心不再演示
3.5.1 A项目改造成微服务
A项目前端代码
<!-- 正常请求 -->
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://127.0.0.1/api-b/getBInfo",
dataType : "json",
success : function(data) {
alert(data["retCode"]);
},
error : function() {
alert('fail');
}
});
});
</script>
A配置文件
server:
port: 8080
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
application:
name: web-a
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8100/eureka/
3.5.2 B项目改造成微服务
B的配置文件
server:
port: 8081
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
application:
name: web-b
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8100/eureka/
3.5.3 网关配置文件
###注册 中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8100/eureka/
server:
port: 80
###网关名称
spring:
application:
name: service-zuul
#### 配置网关反向代理
zuul:
routes:
api-a:
### 以 /api-a/访问转发到web-a
path: /api-a/**
serviceId: web-a
api-b:
### 以 /api-b/访问转发到web-b
path: /api-b/**
serviceId: web-b
效果演示:通过网关路由到了web-a服务的jsp页面,然后调用http://127.0.0.1/api-b/getBInfo,通过路由调用web-b服务的接口