网站跨域解决方案
1,什么是网站跨域
两个项目中之间的相互通讯,如果通过ajax 去访问,如果访问的域名和自己服务的域名不一致,浏览器会认为有安全问题,无法获取到返回结果
2,模拟网站跨域
(1)模拟域名 在 C:\Windows\System32\drivers\etc\hosts
127.0.0.1 crossdomain_a.com
127.0.0.1 crossdomain_b.com
(2) crossdomain_a 项目,启动A项目,访问http://crossdomain_a.com:8080/index 就会走到index 页面,会调用index 的js,通过ajax 访问B项目
@Controller public class indexController { @RequestMapping("/index") public String index(){ return "index"; } }
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <title></title> </head> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $.ajax({ type : "GET", async : false, url : "http://crossdomain_b.com:8081/index", dataType : "json", success : function(data) { alert(data["errorCode"]); }, error : function() { alert('fail'); } }); }); </script> <body> </body> </html>
crossdomain_b 项目
@RestController
public class indexController {
@RequestMapping("/index")
public String index(){
return "A项目正在访问B项目";
}
}
结果:如下图,结果没有返回,并且报错 No 'Access-Control-Allow-Origin' header is present on the requested resource
3,网站跨域解决方案,并且比较优缺点
解决方案一:使用设置响应头允许跨域
之前报错 No 'Access-Control-Allow-Origin' header is present on the requested resource,所以设置下
每次服务的单独请求,可以放在过滤器中去实现
@RestController public class indexController { @RequestMapping("/index") public Object index(HttpServletResponse response){ response.setHeader("Access-Control-Allow-Origin", "*"); Map<String, Object> result = new HashMap<String, Object>(); result.put("errorCode", "200"); result.put("errorMsg", "登陆成功"); return result; } }
解决方案二:Jsonp,只能满足get 请求,无法满足post 请求
crossdomain_a 的web 页面改为:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <title></title> </head> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $.ajax({ type : "GET", async : false, url : "http://crossdomain_b.com:8081/index", dataType : "jsonp", jsonp : "jsonpCallback",//服务端用于接收callback调用的function名的参数, success : function(data) { alert(data["errorCode"]); }, error : function() { alert('fail'); } }); }); </script> <body> 我是A项目,正在调用B项目 </body> </html>
同时将服务端封装数据模拟json 格式,同时需要返回jsonpCallback,代码如下:
@RestController public class indexController { @RequestMapping(value = "/index", method = RequestMethod.GET) public void index(HttpServletResponse response,String jsonpCallback) throws IOException{ JSONObject root = new JSONObject(); root.put("errorCode", "200"); root.put("errorMsg", "登陆成功"); response.setHeader("Content-type", "text/html;charset=UTF-8"); PrintWriter writer = response.getWriter(); System.out.println(jsonpCallback + "(" + root.toString() + ")"); writer.print(jsonpCallback + "(" + root.toString() + ")"); writer.close(); } }
通过resonse 的PrintWriter 对象将数据返回
jsonp 基础知识了解:
什么是JSONP? 先说说JSONP是怎么产生的: 其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,着用自己的方式来阐释一下这个问题,看看是否有帮助。 1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准。 2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)。 3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。 4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。 5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。 6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。 7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
服务端jsonp 数据格式:
1. 服务端 JSONP 格式数据 如客户想访问 : https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction。 假设客户期望返回数据:["customername1","customername2"]。 真正返回到客户端的数据显示为: callbackFunction(["customername1","customername2"])。 服务端文件 jsonp.php 代码为:
解决方案三:使用HttpClient进行内部转发
详解:其本质就是本来是通过ajax访问域名crossdomain_b.com 的项目,出现了跨域问题。现在就是在crossdomain_a 服务器端封装了一个方法,由这个方法通过发送http 请求到crossdomain_b 的项目。
服务器端不存在跨域现象
缺点:相当于发送了两次请求
代码如下:crossdomain_a 添加转发的方法:
@RequestMapping("/tocrossdomain_b") @ResponseBody public JSONObject tocrossdomain_b(){ JSONObject result = HttpClientUtils.httpGet("http://crossdomain_b.com:8081/index"); System.out.println("result:" + result); return result; }
crossdomain_b实际执行的方法
@RequestMapping(value = "/index", method = RequestMethod.GET) public JSONObject index(HttpServletResponse response, String jsonpCallback) throws IOException { JSONObject root = new JSONObject(); root.put("errorCode", "200"); root.put("errorMsg", "登陆成功"); return root; }
crossdomain_a 的web页面:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <title></title> </head> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $.ajax({ type : "GET", async : false, url : "http://crossdomain_a.com:8080/tocrossdomain_b", dataType : "json", success : function(data) { alert(data["errorCode"]); }, error : function() { alert('fail'); } }); }); </script> <body> A在调用B </body> </html>
解决方案四:使用Nginx搭建Api 接口网关
详解:这个思路是和方法三相同点都是在服务器端进行处理,方案三用HttpClient在内部做转发,相当于又调用了一次接口,用Nginx 来实现,相当于用Nginx 来做反向代理,转发到crossdoamain_b,走的也是服务器端
具体的实现,就是配置下Ngix,可参考Nginx 配置
https://www.cnblogs.com/pickKnow/p/11281506.html
解决方案五:使用springcloud 的zuul 搭建Api 接口网关
详解:思路是和Nginx 实现是一样的,只不过一个是用Nginx实现接口网关,一个是用zuul 实现接口网关
总结:大公司小公司,都可以使用Nginx ,zuul 实现
要求简单方便,可以用设置响应头,允许跨域