欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-10 16:17:57
...

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

文章内容:

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

一、跨域原因

现在的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反向代理
跨域问题——知其然知其所以然
跨域问题——知其然知其所以然