跨域详解!前后端分离解决跨域问题

一、为什么会出现跨域问题

由于浏览器的同源策略限制

同源策略(Same-Origin-Policy)是一种约定,它是浏览器最核心、最基本的安全功能。如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的JavasSript脚本和另外一个域的内容进行交互。

同源(即指在同一个域):两个页面具有相同的协议(protocol),主机(host)和端口号(port)

  • 协议相同
  • 域名相同
  • 端口相同

浏览器执行JavaScript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行.

服务器之间的请求是不存在跨域问题的!

在这里插入图片描述

【明确因果】有浏览器存在,才会产生跨域问题。而浏览器浏览器最核心、最基本的安全功能是同源策略。基于这种策略下,不予许访问非访问非同源的资源(即跨域请求)


二、什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url被请求页面url是否跨域原因
http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/http://www.baidu.com/跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域.

例如:

  • 在本站点上请求本站点的资源,不会报错
  • 在本站点上请求其他站点的资源,报错(在Google上请求百度的资源)
    在这里插入图片描述

【报错信息】Access to fetch at ‘https://www.baidu.com/’ from origin ‘https://www.google.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.


三、非同源限制

【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB

【2】无法接触非同源网页的 DOM

【3】无法向非同源地址发送 AJAX 请求


四、跨域问题的解决方式

Jsonp前后端配合

JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE)

【缺点】

  • 只支持get请求,不支持post请求
  • 只支持跨域HTTP请求

<script>标签是天然支持跨域的,我们通过<script>标签可引入外部的脚本,即请求别的资源

【核心思想】

网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。

前端修改

①原生实现:

// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
<script src="http://test.com/data.php?callback=dosomething"></script>
 
// 处理服务器返回回调函数的数据
<script type="text/javascript">
    function dosomething(res){
        // 处理获得的数据
        console.log(res.data)
    }
</script>

② jQuery ajax:

$.ajax({
    url: 'http://www.test.com:8080/login',
    type: 'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: "handleCallback",    // 自定义回调函数名
    data: {}
});

③ Vue.js

this.$http.jsonp('http://www.domain2.com:8080/login', {
    params: {},
    jsonp: 'handleCallback'
}).then((res) => {
    console.log(res); 
})
后端修改

服务端不在返回的是一个JSON格式的数据,而是返回一段JS代码,将JSON的数据以参数的形式传递到这个函数中,而函数的名称就是callback参数的值。

将JS对象解析成JSON,再传入调用函数。

Jsonp虽然有效,但是处理具有局限性。我们一般都采用CORS


CORS

CORS是一种W3C标准,全称是"跨域资源共享(Cross-origin resource sharing)"。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了同源使用的限制。

实现CORS很简单,就是在服务端加一些响应头,对前端来说是无感知的。

详解响应头

Access-Control-Allow-Origin

  • 该字段必填
  • 它的值要么是请求时Origin字段的具体值,要么是一个*,表示接受任意域名的请求。

Access-Control-Allow-Methods

  • 该字段必填
  • 它的值是逗号分隔的一个具体的字符串或者*,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

Access-Control-Expose-Headers

  • 该字段可选
  • CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

Access-Control-Allow-Credentials

  • 该字段可选
  • 它的值是一个布尔值,表示是否允许发送Cookie.默认情况下,不发生Cookie,即:false。对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json,这个值只能设为true。如果服务器不要浏览器发送Cookie,删除该字段即可。

Access-Control-Max-Age

  • 该字段可选
  • 用来指定本次预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求。

下面,我们使用CROS通过SpringBoot来在解决后端跨域问题


5. SpringBoot解决

【方式一】全局配置

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 所有的当前站点的请求地址,都支持跨域访问
                .allowedOrigins("*") // 所有的外部域都可跨域访问。 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") // 当前站点支持的跨域请求类型是什么
                .allowCredentials(true) // 是否支持跨域用户凭证
                .maxAge(3600) // 超时时长设置为1小时。 时间单位是秒。
                .allowedHeaders("*"); //获取所有请求头字段
    }
}

WebMvcConfigurerAdapter在Spring5.0已经被标记为Deprecated,推荐使用过滤器

基于过滤器的方式,方式简单明了,就是在response中写入这些响应头

import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin","*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        chain.doFilter(req, res);
    }
}

【方式二】@CrossOrigin

@CrossOrigin细粒度单个请求控制

public class GoodsController {

    @CrossOrigin(origins = "http://localhost:8181")
    @GetMapping("/list")
    public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {}
}   

@CrossOrigin注解,点开注解

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {}

从元注解@Target可以看出,注解可以放在method、class等上面,类似RequestMapping,也就是说,整个controller下面的方法可以都受控制,也可以单个方法受控制。

也可以得知,这个是最小粒度的CROS控制办法了,精确到单个请求级别。

Nginx反向代理解决跨域问题

🔗nginx怎么解决跨域

【解决原理】
在这里插入图片描述

nginx作为反向代理服务器,就是把Http请求转发到另一个或者一些服务器上。通过把本地一个url前缀映射到要跨域访问的web服务器上,就可以实现跨域访问。


【文章参考】

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值