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,需要在这里指定)

相关文章
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
1031 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
1714 0
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
1088 0
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
1074 0
|
11月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
476 0
Spring中最大化@Lazy注解,实现资源高效利用
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
956 0
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
423 0
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
502 0
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
1160 0