前端请求跨域问题总结

/ 默认分类 / 0 条评论 / 853浏览

前端请求跨域问题总结

从大一开始接触前后端技术开始做自己的小网页时就遇到过跨域的问题,我深刻记得,那次我偶然间发现一个接口可以获取免费的二次元高质量图片(是在浏览器中抓包发现的),当时兴致满满的写好了前端,准备将返回的图片都展示出来,这是我第一次遇到跨域的问题,查看控制台后就是看到那一串似曾相识的ajax跨域报错提示,之后上网搜索后才搞明白,从此js的同源策略给我留下了很深的印象.在之后四年大学时光中,我也不断地遇到并解决这样的问题,一直到现在在工作上也仍然在继续做着这样的工作,今天我想来总结一下,前端请求跨域的问题,欢迎各位大佬指点

一.什么是跨域

ajax在请求访问资源时,==协议+主机名+端口号== (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。 另外还有一个就是同源策略的概念,同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能.同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。

简单点说就是,现在一个A站点在浏览器网址栏显示的是www.whatsoft.cn,那么这个网站上的js需要通过ajax访问B站点的资源,B站点的资源的网址是www.sogolang.com,那么这个时候就会出现跨域问题进而导致这个ajax请求会失败报错:

XMLHttpRequest cannot loadhttp://www.sogolang.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

是的上面的信息相信你一定看过,这就是跨域的典型报错信息.那么哪些情况才算跨域呢?看下表

二.怎样解决跨域问题

2.1 前提介绍

大家应该已经产生了疑问,既然域名不同会产生跨域问题导致js无法执行,那为什么我再网站中引入cdn的js或者css文件时,域名也是不同但是是可以访问这个资源的呢?其实这正是下面需要介绍的,img、iframe 、script等标签是例外的,不会受到js同源策略的影响,并且这也是解决跨域问题的其中一个办法的基础,下面我们就来一一介绍一下

2.2 js中跨域请求的过程

ajax在发送请求时,如果发现请求的域名是跨域的,那么会首先发送一次试探请求,请求方式也就是option,option请求获得的响应是不能带正文的,只会有响应头,这个请求会先帮我们检查先这个跨域的接口是否是允许我们站点跨域访问的,如果不支持那么就会拒绝访问,报错跨域问题.如果发现这个接口是允许我们跨域访问的,那么接下来ajax就会发送正式的请求,去这个接口获取数据.

2.3 解决方法

第一种 后端允许跨域

后端的接口在响应时,手动在响应头加上

response.addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问 
response.addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

其实这在很多后端的框架中都有直接的全局配置的支持,比如springboot中就可以这样配置允许跨域

/**
 * @author zuohui@whatsoft.cn
 * @date 2019/6/23 - 11:18
 */
@Configuration
public class MyConnfig {

     @Bean
    public WebMvcConfigurer register() {
        WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
      
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                        registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowCredentials(true)
                        .allowedMethods("*")
                        .maxAge(3600);
            }

        };
        return webMvcConfigurer;
    }
}

第二种 jsonp

使用jsonp作为数据传输格式

那么前端的请求方式是这样的:


     $.ajax({
			type : "GET",
			async : false,
			url : "http://www.sogolang.com/api?name=666",
			dataType : "jsonp",//数据类型为jsonp  
			jsonp : "jsonpCallback",//告诉服务端,我的jsonp的回调函数放在哪个参数下,你取这个就能拿到名字,你回传给我的时候就用这个名字,这样我就能拿到数据
			success : function(data) {
				alert(data["userName"]);
			},
			error : function() {
				alert('fail');
			}
		});
		
		
		
		或者,但是一般可以直接使用默认的即可
		
		
		$.ajax({
        url:"http://crossdomain.com/services.php",
        type:'get',//注意:跨域请求是只能是get请求不能使用post请求
        dataType:'jsonp',
        data:'',
        jsonp:'callback',//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
        jsonpCallback:"success",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
        success:function(result) {
            console.log(result);
        },
        timeout:3000
        });

说明:
① dataType改为jsonp
② jsonp : "jsonpCallback"这是指发送到后端实际为http://www.sogolang.com/api?name=666&jsonpCallback=jQueryxxx
③ 后端获取get请求中的jsonpCallback
④ 构造回调结构

后端也就是这样的

        //获取前端即将回调的函数名字
        String jsonpCallback = request.getParameter("jsonpCallback");
		//构造回调函数格式jsonpCallback(数据)
		resp.getWriter().println(jsonpCallback+"("+jsonObject.toJSONString()+")");

ps:那么问题来了,为啥这样传递就可以实现跨域了呢?下面我们来看一下jsonp解决跨域的原理,其实上面我已经说过了,jsonp就是利用 了img、iframe 、script等标签这种跨域例外的特性,这些标签可以通过src属性请求到其他服务器上的数据。利用<script>标签的开放策略,我们可以实现跨域请求数据,当然这需要服务器端的配合。 jsonp会通过动态的添加<script>标签来请求组装后的请求url

  当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用 JSONP模式来请求数据的时候服务端返回的是一段可执行的JavaScript代码。==因为jsonp 跨域的原理就是用的动态加载<script>的src ,所以我们只能把参数通过url的方式传递,所以jsonp的 type类型只能是get !==   

下面是一个例子:

首先前端的ajax是这样的:

$.ajax({
    url: 'http://www.golang.com/api', //不同域
    type: 'GET',                                                        
    data: {
        'name': '666'
    },
    dataType: 'jsonp',                                             
    jsonp: 'jsonpCallback',                                     /
})

那么执行这个ajax时,会先构造好真实的请求路径

http://www.sogolang.com/api?jsonpCallback=jQuery202003573935762227615_1402643146875&name=666

然后动态创建<script>标签,并将连接注入

<script type="text/javascript" src="http://www.sogolang.com/api?jsonpCallback=jQuery202003573935762227615_1402643146875&name=666">
</script>

这样后端就会将数据封装到回调函数的实参里面返回给前端,前端内部会进行处理,在ajax中的请求成功函数中拿到数据

好的整个过程就是这样,现在大家应该了解了jsonp的原理了吧.

第三种 后端http客户端访问

前端调用本域下的接口,在接口中后端使用http客户端,比如apache的httpclient等来模拟浏览器调用这个跨域的接口,然后将返回的数据返回给本域的前端即可,这个方法很容易理解,这里不多哔哔了

第四种 nginx转发

这个方法也很好理解,我们来直接看下配置

server {
        listen       80;
        server_name  www.sogolang.com;
        location /A {
		    proxy_pass  http://www.whatsoft.cn/api1;
			index  index.html index.htm;
        }
		location /B {
		    proxy_pass  http://www.whatsoft.cn/api2;
			index  index.html index.htm;
        }
    }

不难看出,本域下的js还是访问的本域的接口,只不过,在服务器的nginx中,我们配置了按照不同的请求url,来转发到不同的异域下的接口

三.总结

好的,跨域问题就像这样总结一下,有不准确的地方各位大佬请在博客下方留言指正.