网站跨域解决方案

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 实现

           要求简单方便,可以用设置响应头,允许跨域

 

posted @ 2019-08-13 14:29  Chris,Cai  阅读(509)  评论(0编辑  收藏  举报