跨域请求的几种实现方式

参考文章:
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;  
 }  
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值