CORS跨域资源共享(一):模拟跨域请求以及结果分析,理解同源策略【享学Spring MVC】(上)

简介: CORS跨域资源共享(一):模拟跨域请求以及结果分析,理解同源策略【享学Spring MVC】(上)

前言


CORS的全称是:跨域资源共享(Cross-origin resource sharing),它是浏览器的一个技术规范。

浏览器自己是可以发起跨域请求的(比如你可以外链一个外域的图片或者视频),但是Javascript脚本是不能跨域去获取这些资源的内容的。传统的ajax请求只能获取在同一个域名下的资源,但是Html5打破了这个限制:允许ajax发起跨域请求。跨域的解决方案有多种:JSONP、Flash、IFrame等,当然还有今天的主菜CORS。


我有理由相信若你在前端使用过Ajax,你100%遇见过如下图这样的报错


image.png


若你看到这样的报错,那么此次你的请求返回数据是失败的(请务必理解这句话)。但是,但是,但是若你查看调试工具的Network栏,发现这个URL请求的response是有返回值的(并且http状态码是200,表示请求被服务端正常处理了),形如这样:


image.png


image.png


看似相悖的结果,这到底怎么回事???本文就告诉你答案

同源策略


同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。该策略是浏览器最核心也最基本的安全功能,同源指的是:同协议、同域名、同端口。


它的核心思想可以理解为:我只相信我同一个域的资源,来自于其它域的我都不可信,所以同源策略主要还是出于安全考虑的~


JavaScript或Cookie只能访问同源(同协议、同域名、同端口下的内容。


CORS


CORS它是W3C(万维网联盟)的标准,它定义了在跨域访问资源时浏览器和服务器之间如何通信。它是为突破同源策略的限制而出现的一种官方标准的跨域解决方案。在实战场景中,跨域场景太为常见了(特别是当下前后端分离的开发模式),因此深入理解CORS变得就异常的重要了(反倒前端工程师不用太了解)。


若想实现CORS机制的跨域请求,是需要浏览器和服务器同时支持的。关于浏览器对CORS的支持情况:现在都9012年了,so可以认为100%的浏览器都是支持的,再加上CORS的整个过程都由浏览器自动完成,前端无需做任何设置,所以你的ajax原来怎么用现在还是怎么用,它对前段开发人员是完全透明的。

所以呢,让此种机制生效的关键就在于服务器端,so作为服务端开发的我们,必须要玩转CORS才可正常实现跨域通信。


CORS机制的指导思想:自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否


为何需要跨域请求???


这是跨域请求产生的背景,最主要是随着互联网的发展,忘了改善网络应用程序的环境增强其功能,开发人员要求浏览器供应商允许跨域请求,能带来如下好处:


  • javascript可以使用ajax方式跨域访问资源
  • CSS可以使用@font-face跨域调用字体
  • 通过canvas标签,绘制图表和视频


由此可见:跨域不仅仅是ajax的专属


本地模拟跨域请求以及结果分析


上面都是成套成套的理论知识,过于抽象。那接下来我就是要通过本地的实例来模拟出跨域请求,从而依托于案例分析CORS各种不同的case情况下的结果分析。


1、写一个前端HTML页面放于idea(idea可充当静态web服务器)


<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>CORS跨域</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<div style="text-align:center;margin-top: 100px;font-size: 60px;color: brown;cursor: pointer;">
    <span onclick="sendAjaxReq()">发送Ajax请求</span>
</div>
<script type="text/javascript">
    function sendAjaxReq() {
        $.ajax({
            type: "GET",
            // contentType: "application/json",
            url: "http://localhost:8080/demo_war_war/test/cors,
            success: function (message) {
                console.log("成功!" + message);
            },
            error: function (a, b, c) {
                console.log("失败!" + a.statusText);
            }
        });
    }
</script>
</body>
</html>


2、写一个控制器Controller处理页面发送的ajax请求


@RestController
public class CorsController {
    @GetMapping("/test/cors")
    public Object testCors() {
        return "hello cors";
    }
}


3、利用idea的web服务器能力运行html页面,如下截图(本例使用的是标准的63342静态web端口)


image.png


请注意这个页面的访问地址的是http://localhost:63342...,而点击这个"发送Ajax请求"按钮要发送的地址是http://localhost:8080...,两者端口号不一样说明是不同的域,因此此ajax请求它必定属于跨域请求(CORS请求)


4、点击发送按钮,查看控制台的结果

这个case的结果请完全参照文首的几张截图,此处就省略了


Tips:如果域名连不上服务端(比如服务端木有启动),它的报错一般都会是网络连接方面的问题,形如:GET http://localhost:8080/demo_war_war/test/cors net::ERR_CONNECTION_REFUSED,请注意区分~


如上结果,命名返回了200但浏览器偏偏还是报跨域异常,我相信这个让你感觉到十分的诧异和不解,那么接下来就围绕它来解释通这个问题。

但在我解释此现象之前,必须先要弄明白两个非常重要的CORS请求类型:简单请求,非简单请求(说明:这两种请求都属于CORS请求,这是大前提)。


简单请求、非简单请求


CORS发送出来的请求分为两种:


  1. 简单请求。需要同时满足下面三个要求1. 请求方法只能是GET、POST、HEAD2. Content-Type只能是三个值的任意一个application/x-www-form-urlencoded、multipart/form-data、text/plain(备注:若使用jquery的ajax发送请求,没指定Content-Type的情况下,默认它的值是application/x-www-form-urlencoded。源生的ajax请求请手动显示指定)3. 无自定义请求头(除了Accept、Content-Type等等一些内置的头之外的头都叫自定义)
  2. 非简单请求。除了简单请求之外都是它(带预检,也就是我们常见的OPTIONS请求)。


很显然,不满足简单请求三大要求的便都是非简单请求喽。在实际生产应用场景中我们最为常见的非简单请求场景大致有如下三种case:


  1. ajax发送put、delete请求
  2. 发送json格式数据(Content-Type为application/json)
  3. 自定义请求头(比如自定义鉴权请求头Authorization)


简单请求


对于这种请求,浏览器是直接发出请求,它的特点是:浏览器自动给加上一个Origin的请求头,表示这个请求的来源(来自哪个源)。

比如上面案例的请求,它完全符合简单的请求的三大要求,所以它是一个简单请求,浏览器自动给它加上的头是:Origin: http://localhost:63342。

服务端可拿到这个Origin源,然后判断服务端是否能够接受这个源从而决定是否同意这次请求(不同意or同意):


  • 不同意:服务器会返回一个正常的HTTP回应(响应头里木有Access-Control-Allow-Origin这个头),浏览器发现木有这个头,就抛出一个错误XMLHttpRequest,进而进入ajax的onerror回到方法里(这就是为何你明明看到http状态码是200,response也有返回值,但偏偏你ajax里就是进入的error的原因~),它的现象是:服务器正常返回了资源,但浏览器拒绝接收了。
  • 同意:服务器的响应里会多出下面详解的几个响应头,从而回调ajax的onsuccess方法,这就是真正意义上的成功了,浏览器也接收了这个返回结果。


和简单请求相关的3个响应头如下:

Access-Control-Allow-Origin


该响应头是服务器必须返回的。它的值要么是请求时Origin的值(可从request里获取),要么是*这样浏览器才会接受服务器的返回结果。


Access-Control-Allow-Credentials

该响应头非必须,值是bool类型,表示是否允许发送Cookie


  • true:表示服务器允许你浏览器把cookie发给我(若服务器想获取Cookie的,请务必设置此值)
  • false :请注意此字段只能设置为true,若不允许发送cookie,不要设置此响应头即可

Tips:浏览器端默认情况下,Cookie不包括在CORS请求之中,若你想让浏览器带上Cookie,有需要的请自行研究一番吧~


Access-Control-Expose-Headers

该响应头非必须。顾名思义它要把response中的哪些头暴露给浏览器,让它可以获取到(默认情况下浏览器的XMLHttpRequest对象的getResponseHeader()方法只能获取到那些Cache-Control、Expires等等几个标准的响应头,若需要拿其它key,需要在这里指定)

相关文章
|
人工智能 前端开发 JavaScript
webpack-dev-server代理后端一直报CORS跨域或500错误
在Vue项目中使用Webpack的devServer代理后端接口时,遇到500错误。问题根源在于浏览器请求中携带的Origin头导致服务器报错,而Postman测试正常。通过分析发现,调整或移除Origin头可解决问题。解决办法包括:1) 在代理配置中添加正确的Origin头;2) 删除请求中的Origin头。文章还深入解析了Origin头的作用及changeOrigin配置的实际意义,并附带相关文档链接,帮助开发者更好地理解与解决类似跨域问题。
916 20
|
9月前
|
安全 API PHP
PHP中实现CORS跨域资源共享的方法
通过这种方式,你可以在PHP应用中灵活地实现CORS,以支持跨域Web应用的需求。
611 15
|
10月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
374 0
Spring中最大化@Lazy注解,实现资源高效利用
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
1013 0
|
JSON 缓存 前端开发
对CORS(跨域)的一些见解
CORS(跨域资源共享)是W3C标准,用于解决AJAX跨源请求限制。浏览器与服务器需共同支持CORS,浏览器自动处理请求头,开发者无需额外操作。CORS分为简单请求与非简单请求:简单请求满足特定条件(如方法为GET/POST/HEAD且头信息有限制),浏览器直接发送;非简单请求需先进行“预检”请求(OPTIONS方法),确认服务器允许后才发送实际请求。服务器回应需包含Access-Control-Allow-Origin等字段,以控制跨域访问权限。
384 10
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
575 0
|
JSON Java 数据安全/隐私保护
springboot解决跨域问题
项目中前后端分离部署,所以需要解决跨域的问题
453 0
|
Java Spring
springboot解决跨域问题
springboot解决跨域问题
311 0
springboot解决跨域问题
|
12月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1415 0
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1309 0