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")@ResponseBodypublic 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: .jspapplication: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: .jspapplication: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-apath: /api-a/**serviceId: web-aapi-b:### 以 /api-b/访问转发到web-bpath: /api-b/**serviceId: web-b
效果演示:通过网关路由到了web-a服务的jsp页面,然后调用http://127.0.0.1/api-b/getBInfo,通过路由调用web-b服务的接口