跨域问题——知其然知其所以然

最近做项目前后端分离才第一次遇到跨域问题,作为渣渣,当然是虚心学习网络上一些前辈的经验,以下是自己的总结。哪个地方有错,还望告知,定虚心请教。

文章内容:

  • 跨域原因
  • 解决思路
  • 解决方法

一、跨域原因

现在的web项目基本是前后端分离,前端调用后端接口,如果前后端代码不属于同一个域(同一域名同一端口号),就会产生跨域问题。
常见的跨域问题的报错提示是:
Failed to load “被调用方域名”: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘调用方域名’ is therefore not allowed access.
这里写图片描述

那么,为什么会发生AJAX跨域问题?

  • 浏览器限制
  • 请求跨域访问不同域
  • XHR(XMLHttpRequest)请求

当上述三个条件同时满足时,就触发了跨域问题啦~

二、解决思路

这里写图片描述

三、解决方法

1.去掉浏览器限制(以下用chrome做演示)

这种方法并不具有可行性,你没办法让浏览器都这样或者每个用户自己实现。
启动chrome时加 --disable-web-security 禁止它做跨域校验。
这里写图片描述

2.使用JSONP解决

首先来了解一个JSOP是什么?
JSONP是一个非官方协议,约定发送请求的参数中如果包含指定的参数,默认为callback.即JSONP请求,服务器发现其指定参数时,就会把返回值由原来的JSON对象改成JS代码。

JSONP通过动态创建JS在JS中发出请求,用完就销毁,js代码的内容是函数调用的形式,它的函数名是callback的值,它的函数的参数是原先json对象。
其JS函数后面带着时间戳防止被浏览器缓存。此时Content-Type=application/javascript

使用JSONP时服务器端代码需要进行改动(这里用Java代码示例)
这里写图片描述

JSONP的弊端:

  • 需要改动服务器端
  • 只支持GET(因为是动态创建script,script只能GET方法)
  • 发的不是XHR请求(现在XHR有各种特性用于前端开发)

下面,我们来谈谈跨域问题主流的两种解决方式吧

3.被调用方解决

  • Filter解决方案
  • nginx解决方案
  • apache解决方案
  • Spring框架解决方案

1.Filter解决方案
采用这种解决方案之前先来看看下面几个问题。

浏览器对于请求是先执行还是先判断是否跨域?
先执行。

1.是不是所有请求都是先执行呢?
回答这个问题需要弄明白简单请求和非简单请求
浏览器对于简单请求,就是先执行后判断。对于非简单请求,它会先发一个OPTIONS预检命令,检查通过再执行。

2.那么简单请求和非简单请求有哪些呢?
常见的【简单请求】:GET、HEAD、POST
    请求header里面
        无自定义头
        Content-Type为以下几种:
            text/plain
            multipart/form-data
            application/x-www-form-urlencoded
常见的【非简单请求】:
    PUT、DELETE的ajax请求
    发送json格式的ajax请求
    带自定义头的ajax请求

3.浏览器如何判断?
浏览器发现请求是跨域的时候,它会在当前请求的请求头中增加origins字段,然后等请求响应回来,
浏览器会检查响应头中是否存在允许跨域的信息,没有就报错。

4.带Cookie的跨域——Access-Control-Allow-Origin: *是否满足所有跨域场景?
请看下图:

这里写图片描述

答案显然是否,对于带Cookie的跨域请求必须明确指定Access-Control-Allow-Origin的域名,
并设置Access-Control-Allow-Credentialstrue

最后,来看下Filter解决方案的示例代码吧。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletResponse res = (HttpServletResponse) response;

    HttpServletRequest req = (HttpServletRequest) request;

    String origin = req.getHeader("Origin");

    if (!org.springframework.util.StringUtils.isEmpty(origin)) {
        //带cookie的时候,origin必须是全匹配,不能使用*
        res.addHeader("Access-Control-Allow-Origin", origin);           
    }

    // 允许所有请求方法
    res.addHeader("Access-Control-Allow-Methods", "*");

    String headers = req.getHeader("Access-Control-Request-Headers");
    // 支持所有自定义头
    if (!org.springframework.util.StringUtils.isEmpty(headers)) {
        res.addHeader("Access-Control-Allow-Headers", headers);         
    }

    res.addHeader("Access-Control-Max-Age", "3600");

    // enable cookie
    res.addHeader("Access-Control-Allow-Credentials", "true");

    chain.doFilter(request, response);
}

2.nginx解决方案
这里写图片描述

3.apache解决方案
这里写图片描述

4.Spring框架解决方案
controller类上加@CrossOrigin注解

4.调用方解决——隐藏跨域

调用方解决跨域问题只有一种方式,就是利用Http服务器进行隐藏跨域啦,下面给出nginx和apache两个服务器隐藏跨域的配置方式
1.反向代理——nginx配置
调用方设置nginx反向代理
这里写图片描述
这里写图片描述

2.反向代理——Apache配置
调用方设置Apache反向代理
这里写图片描述
加图-------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值