什么是跨域请求,为什么用跨域
浏览器从一个域名的网页去请求另一个域名的资源时,域名,端口,协议任何一个不同,都算是跨域请求
域名是一个网站的唯一标识,一个域名代表着一个网站以及其对应的服务
服务间调用可以使用基于soa思想的rpc调用,也可以是webservice等等
那么从前端页面发起的调用,就会用到跨域
说跨域就必须先说浏览器的同源策略(SOP(same origin policy)),同源指域名,端口,协议同时相同.
同源策略就是为了保护浏览器免受XSS、CSFR等攻击,
但有时候又需要浏览器进行调用,域名不同最为常见,那么就需要用到跨域
跨域的几种解决方案
1、 通过jsonp跨域(JSON with Padding)
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
jsonp原理
JSONP的最基本的原理是:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。
这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了。这样其实"jQuery AJAX跨域问题"就成了个伪命题,jquery $.ajax方法名有误导人之嫌。如果设为dataType: 'jsonp',这个$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。
JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。JSONP即JSON with Padding。由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。
如果要进行跨域请求, 我们可以通过使用html的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。
这种跨域的通讯方式称为JSONP。jsonCallback 函数jsonp1236827957501(....):是浏览器客户端注册的,获取跨域服务器上的json数据后,回调的函数Jsonp的执行过程如下:一.首先在客户端注册一个callback (如:'jsoncallback'), 然后把callback的名字(如:jsonp1236827957501)传给服务器。
注意:服务端得到callback的数值后,要用jsonp1236827957501(......)把将要输出的json内容包括起来,
此时,服务器生成 json 数据才能被客户端正确接收。二.然后以 javascript 语法的方式,生成一个function,
function 名字就是传递上来的参数 'jsoncallback'的值 jsonp1236827957501 .三.最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时javascript文档数据,作为参数,
传入到了客户端预先定义好的 callback 函数(如上例中jquery $.ajax()方法封装的的success: function (json))里。可以说jsonp的方式原理上和<script src="http://跨域/...xx.js"></script>是一致的
(qq空间就是大量采用这种方式来实现跨域数据交换的)。
JSONP是一种脚本注入(Script Injection)行为,所以有一定的安全隐患。
jsonp优缺点
优点:不受同源策略限制;兼容性好(旧版本浏览器也适用);callback回调机制回传结果
缺点:只支持get方式的http请求,有安全隐患(本质是脚本注入)
jsonp的使用(Js/JQuery+SpringMVC)
原生js写法:
假设这个域名是www.aa.com
<script>var script = document.createElement('script');script.type = 'text/javascript';// 传参并指定回调执行函数为onBackscript.src = 'http://www.bb.com:8080/login?user=admin&callback=onBack';document.head.appendChild(script);// 回调执行函数function onBack(res) {alert(JSON.stringify(res));}</script>
jquery写法:
需要注意的是,jquery虽然是以ajax的方式写的jsonp,但我们一定要清楚,jsonp和ajax没有关系,ajax是严格遵守同源策略的,只不过是jquery封装的时候复用了ajax的格式而已
jQuery.ajax({type: "get",async: false,url: "http://www.bb.com:8080/login?user=admin",dataType: "jsonp",jsonpCallback: "onBack", //指定回调函数名称,不写的话也行,会有个随机的success: function(result){}
});
后台controller:
@Controller
public class TestController {@RequestMapping("login")public void login(HttpServletResponse response, HttpServletRequest request){Map<String, Map<String, String>> res = null;try {//清除浏览器缓存(习惯写)response.setHeader("Pragma", "No-cache");response.setHeader("Cache-Control", "no-cache");response.setHeader("Cache-Control", "no-store");response.setDateHeader("Expires", 0);response.setCharacterEncoding("UTF-8");//必须写,这个要回传回去String callback = request.getParameter("callback");//这个是简单的防xss漏洞方法callback = replaceInvaidStr(callback);//获得get请求的参数String user= request.getParameter("user");//调用service服务,获得requestUser user = xxxService.getUser(user);//拼接jsonp格式的返回值String output = callback + "(" + JsonUtil.toJSONString(res) + ")";//response输出writeHtml(response,output);} catch (Exception e) {//异常处理}}/*** response输出*/private void writeHtml(HttpServletResponse response, String html) {response.setContentType("application/json;charset=UTF-8");PrintWriter pw = null;try {pw = response.getWriter();pw.write(html);pw.flush();pw.close();} catch (Exception e) {logger.error("StockController writeHtml 写json失败..." + e);if (pw != null) {pw.close();}}/*** 防xss漏洞用* @param str* @return*/public String replaceInvaidStr(String str){if(str!=null){str=str.replace('|',' ');str=str.replace('#',' ');str=str.replace('$',' ');str=str.replace('%',' ');str=str.replace('@',' ');str=str.replace('\'',' ');str=str.replace('\"',' ');str=str.replace('<',' ');str=str.replace('>',' ');str=str.replace('@',' ');str=str.replace('\\',' ');str=str.replace('+',' ');return str;}return "";}
}