参考文章:
http://www.jianshu.com/p/a63c29e0c863
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html#m5
http://www.cnblogs.com/bninp/p/5694277.html
跨域是浏览器的一种安全策略,是浏览器自身做的限制,不允许用户访问不同域名或端口或协议的网站数据。
只有域名(主域名【一级域名】和二级域名)、端口号、协议 完全相同的时候,才允许通信。
通过前端实现跨域解决方案有:document.domain + iframe, window+name, HTML5 postMessage 。。。。详见该篇文章
还有这篇
本文讲述几种通过后端实现跨域的解决方案:
1.jsonp
最常见的一种跨域方式,其背后原理就是利用了script标签不受同源策略的限制,在页面中动态插入了script,script标签的src属性就是后端api接口的地址,并且以get的方式将前端回调处理函数名称告诉后端,后端在响应请求时会将回调返还,并且将数据以参数的形式传递回去。
前端js:
//1.jsonp 跨域(涉及到前后端) 原理是利用script标签不受同源策略的限制 在页面中动态插入script标签 标签的src即为请求url的地址
//并将前端获取到请求数据之后的回调函数作为参数附加在url地址后
var script = document.createElement('script');
//注意 会有用户名密码 验证的问题
script.src = "http://127.0.0.1:6666/tools/jsonpHandler?callback=_callback&username=admin&password=123456";
document.body.appendChild(script);
//回调处理函数
var _callback = function(obj){
for(key in obj){
console.log("JSONP跨域测试 key: " + key + " value: " + obj[key]);
}
}
后端java:
@RequestMapping(value = "jsonpHandler")
public ModelAndView jsonpHandler(HttpServletRequest request, HttpServletResponse response) throws Exception{
String callback = request.getParameter("callback");
logger.info("callback: " + callback);
JSONObject obj = new JSONObject();
obj.put("type", "jsonp");
obj.put("method", "jsonHandler");
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(callback + "(" + JSON.toJSONString(obj) + ")");
return null;
}
2.CORS
Cross-Origin Resource Sharing(跨域资源共享)是一种允许当前域(origin)的资源(比如html/js/web service)被其他域(origin)的脚本请求访问的机制。
当使用XMLHttpRequest发送请求时,浏览器如果发现违反了同源策略就会自动加上一个请求头:origin,后端在接受到请求后确定响应后会在Response Headers中加入一个属性:Access-Control-Allow-Origin,值就是发起请求的源地址(http://127.0.0.1:8888),浏览器得到响应会进行判断Access-Control-Allow-Origin的值是否和当前的地址相同,只有匹配成功后才进行响应处理。
现代浏览器中和移动端都支持CORS(除了opera mini),IE下需要8+
前端js:
//2.CORS 涉及到前后端
$.ajax({
type:"POST",
datatype:"JSON",
data:{
"param1":"param1-value",
"param2":"param2-value",
"username":"admin",
"password":"123456"
},
url:"http://127.0.0.1:6666/tools/corsHandler",
error:function(){
alert("请求失败!");
},
success:function(data){
console.log("CORS跨域测试 data:");
console.log(data);
}
});
后台java:
@RequestMapping(value = "corsHandler")
public ModelAndView corsHandler(HttpServletRequest request,HttpServletResponse response) throws IOException{
String param1 = request.getParameter("param1");
String param2 = request.getParameter("param2");
String requestUri = request.getRequestURI();
String requestUrl = request.getRequestURL().toString();//本域名下请求地址 不是客户端的地址
Integer tempIndex = requestUrl.indexOf(requestUri);
//发出请求的源域名地址
String requestOrigin = requestUrl.substring(0, tempIndex);
if(request.getHeader("Origin") != null ){//跨域请求
requestOrigin = request.getHeader("Origin");
}
logger.info("requestUri: " + requestUri + "; requestUrl: " + requestUrl+ "; requestOrigin: " + requestOrigin);
JSONObject obj = new JSONObject();
obj.put("param1", param1);
obj.put("param2", param2);
//此方法allowOrigin中只能写一个域名 因此改为与list对比的方式实现
/*String allowOrigin = appConfig.getAllowOrigin();
logger.info("allowOrigin: " + allowOrigin);
response.setHeader("Access-Control-Allow-Origin", allowOrigin);*/
response.setContentType("text/html; charset=UTF-8");
if(Constants.allowOriginList.
contains(requestOrigin)){
response.setHeader("Access-Control-Allow-Origin", requestOrigin);
}
response.getWriter().write( JSON.toJSONString(obj));
return null;
}
3.nginx反向代理
此方法最为便利,前后端代码无需做特殊改变,只需在nginx.conf做配置,将本地请求地址转向真正实现请求的url地址。
js代码:
//3.用nginx代理解决跨域问题
$.ajax({
type:"POST",
datatype:"JSON",
data:{
"param1":"nginx-param1-value",
"param2":"nginx-param2-value",
"username":"admin",
"password":"123456"
},
url:"/apis/tools/nginxCrossOrigin",
error:function(){
alert("请求失败!");
},
success:function(data){
console.log("nginxCrossOrigin 跨域测试 data:");
console.log(data);
}
});
java代码:
public ModelAndView nginxCrossOrigin(HttpServletRequest request,HttpServletResponse response) throws IOException{
String param1 = request.getParameter("param1");
String param2 = request.getParameter("param2");
logger.info("param1: " + param1 + "; param2 : " + param2);
JSONObject obj = new JSONObject();
obj.put("param1", param1);
obj.put("param2", param2);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write( JSON.toJSONString(obj));
return null;
}
nginx.conf:
#for cross origin request
server {
#在浏览器中访问源域名地址时 使用端口2222
listen 2222;
#源域名地址下访问/apis/***开头的url地址 会自动将请求转向http://127.0.0.1:14444/***
location /apis {
#$1 第一个()中的值
rewrite ^.+apis/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass http://127.0.0.1:14444;
}
#发出请求的源域名地址
location / {
proxy_pass http://127.0.0.1:12222;
}
}
关于nginx和uwsgi的关系,可参考文章。
(1 )首先nginx 是对外的服务接口,外部浏览器通过url访问nginx,
(2)nginx 接收到浏览器发送过来的http请求,将包进行解析,分析url,如果是静态文件请求就直接访问用户给nginx配置的静态文件目录,直接返回用户请求的静态文件,
如果不是静态文件,而是一个动态的请求,那么nginx就将请求转发给uwsgi,uwsgi 接收到请求之后将包进行处理,处理成wsgi可以接受的格式,并发给wsgi,wsgi 根据请求调用应用程序的某个文件,某个文件的某个函数,最后处理完将返回值再次交给wsgi,wsgi将返回值进行打包,打包成uwsgi能够接收的格式,uwsgi接收wsgi 发送的请求,并转发给nginx,nginx最终将返回值返回给浏览器。
(3)要知道第一级的nginx并不是必须的,uwsgi完全可以完成整个的和浏览器交互的流程,但是要考虑到某些情况
a.安全问题,程序不能直接被浏览器访问到,而是通过nginx,nginx只开放某个接口,uwsgi本身是内网接口,这样运维人员在nginx上加上安全性的限制,可以达到保护程序的作用。
b.负载均衡问题,一个uwsgi很可能不够用,即使开了多个work也是不行,毕竟一台机器的cpu和内存都是有限的,有了nginx做代理,一个nginx可以代理多台uwsgi完成uwsgi的负载均衡。
c.静态文件问题,用django或是uwsgi这种东西来负责静态文件的处理是很浪费的行为,而且他们本身对文件的处理也不如nginx好,所以整个静态文件的处理都直接由nginx完成,静态文件的访问完全不去经过uwsgi以及其后面的东西。
4.后台跨域
前端向本地后台发出请求,后台再向api服务器请求数据,然后再将请求到的数据返回给前端。
后台请求函数实现:
/**
* @param url 请求地址
* @param map 请求参数
* @param charset 字符集
*
* @return map - status 返回状态 1-失败 0-成功 Integer
* @return map - result 失败原因 or 请求成功后api的返回信息 String
*
* */
@SuppressWarnings({ "resource", "deprecation" })
public static HashMap<String,Object> doPost(String url,Map<String,Object> map,String charset){
HttpClient httpClient = null;
HttpPost httpPost = null;
String result = "服务器出错啦!请稍后重试!";
Integer status = 1;
HashMap<String,Object> returnMap = new HashMap<String,Object>();
try{
httpClient = new DefaultHttpClient();
httpPost = new HttpPost(url);
//设置参数
/* List<NameValuePair> list = new ArrayList<NameValuePair>();
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Entry<String,String> elem = (Entry<String, String>) iterator.next();
list.add(new BasicNameValuePair(elem.getKey(),elem.getValue()));
}
if(list.size() > 0){
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,charset);
httpPost.setEntity(entity);
} */
String jsonStr = Json.toJson(map);
// String jsonStr = JSON.toJSONString(map);
logger.info("jsonStr: " + jsonStr);
logger.info("jsonStr2:" + JSON.toJSONString(map));
StringEntity entity = new StringEntity(jsonStr, "UTF-8");
entity.setContentType("application/json;charset=UTF-8");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
if(response != null){
if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()){
HttpEntity resEntity = response.getEntity();
if(resEntity != null){
status = 0;
result = EntityUtils.toString(resEntity,charset);
}
}else{
status = 1;
result = "服务器内部错误导致请求出错!";
}
}else{
status = 1;
result = "服务器内部错误导致请求出错!";
}
}catch(Exception ex){
ex.printStackTrace();
status = 1;
result = "后台出现异常,exception: " + ex.toString();
}
returnMap.put("status", status);
returnMap.put("result", result);
return returnMap;
}