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

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

请求成功案例


为了写出一个完全正确CORS简单请求,基于本例我只需要加一句代码即可:


@GetMapping("/test/cors")
public Object testCors(HttpServerResponse response) {
  // HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN
    response.addHeader("Access-Control-Allow-Origin", "http://localhost:63342");
    return "hello cors";
}


再次点击按钮发送ajax请求结果如下:


image.png


大功告成。服务端不仅仅正常处理了请求,浏览器也接受了返回值。


对于简单请求请务必杜绝这种case:返回的状态码是200(服务端逻辑正常执行且正常返回了),浏览器不会接收结果,而是回调到error方法去~


非简单请求


顾名思义,它比简单请求就要复杂些,不是简单请求的CORS请求都属于"非简单请求"(比如请求方法是PUT或DELETE)。它最大的一个特点是:在发送正式请求通信之前,增加一次HTTP OPTIONS请求,这个请求称之为预检(preflight)请求。


发送OPTIONS预检请求的过程完全由浏览器自动完成,开发者无需关心。


预检请求:它的作用是试探服务端是否能接受真正的请求,若服务器返回的状态码不是2xx而是4xx/5xx的话,那么浏览器将停止发送真正的请求。OPTIONS请求它具有如下特征:


  1. 没有请求body体
  2. 可以有响应body体(比如我们熟悉的:Invalid CORS request)
  3. 安全
  4. 幂等性
  5. 不能缓存,不能在表单里使用


下面先看一个非简单请求的例子,只需要把上例的Ajax注释的contentType放开即可,它便轻松成为了一个非简单请求了:

...
contentType: "application/json",
...


点击发送按钮,结果截图如下:


image.png


image.png


OPTIONS请求返回的状态码是403,所以真实的请求并未发送(network栏只有一个请求~)。浏览器自动添加的请求头中,最重要的仍然是Origin这个头,例如我们生产环境的请求头如下:


image.png


另外两个请求头解释如下(虽然不是十分重要,但也是必须了解的):


Access-Control-Request-Method:该请求头是必须的。发给服务器告知我接下来的真实方法是啥,本例是GET;

  • Access-Control-Request-Headers:非必须(因为可能无自定义的请求头嘛)。若有多个是逗号分隔,告诉服务器我真实请求即将携带的请求头是哪些,本例是content-type这一个请求头;
  • 这些请求头最终都发送给服务器,服务器收到这个预检请求后判断,检查这些头,确认允许跨域与否就可以做出相应的回应了(本例回应403:Forbidden)。


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


Access-Control-Allow-Origin和Access-Control-Allow-Credentials

同上


Access-Control-Allow-Methods


必须的相应头,值是逗号分隔的字符串。表明我服务器可以支持的所有跨域请求的方法~可以用*代替


注:为何返回的不单单是马上要发真实请求的那个方法,而是多个呢???这是为了避免多次"预检"请求,提高效率。后面你可以看到它的功效

Access-Control-Allow-Headers

若请求头中包含Access-Control-Request-Headers,那响应头中这个头就是必须的,否则是非必须的。它的值是逗号分隔的字符串,表示我服务器支持的所有头字段,不限于预检请求中的头字段(但请包含它~)。可以用*代替


说明:若请求头中有Access-Control-Request-Headers,但是没有此响应头/响应头中的值不包含请求头的值。那么出现的奇异现象便是:OPTIONS请求正常200返回,但是真实请求就不会发送了。所以使用时请务必注意~


Access-Control-Max-Age(重要)


非必须。它表示需要缓存预检结果多长时间,单位是秒。比如Access-Control-Max-Age: 600表示将预检结果缓存10分钟,即表示10分钟之内同样的URL将不再发送预检请求。如果值是0表示不用缓存~


Tips:因为它对url生效,所以对那些默认的查询条件取当前时间戳的可千万别这么干了,一般我相信你精确到日期就够了而不用精确到毫秒吧,否则age就不生效了(每次都还得发送预检请求)


当然,你的浏览器也是可以禁用掉这种缓存的。


image.png


请求成功案例


它和简单请求的处理方式是不一样的,因为OPTIONS请求进入不了Handler方法,所以在Controller里向HttpServletResponse里设置请求头是无效的。

因此我们应该把设置相应头信息放在Filter/HandlerInterceptor上才行,本例以Spring MVC的拦截器为例(生产上推荐使用Filter):


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  // 这几个响应头都是可以用*来表示所有的
    response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
    response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*");
    response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
    response.addHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "60");
    return true;
}


请注意:这些添加header只能放在preHandle,放在postHandle/afterCompletion里都将不生效(network里能看到这个头,但是无效果


配置好后,点击按钮发送Ajax请求,结果截图如下:


image.png


image.png


非简单请求跨域成功。从截图的结果上还能看到Access-Control-Max-Age它的功效,它能够减少OPTIONS请求的发送,从而减轻对服务端的访问压力。


如何理解Access-Control-Max-Age对相同URL生效???

为了更好理解这个响应头的作用,我针对性的做出如下试验:


为了测试,我把Access-Control-Max-Age设为了24小时,以保证缓存“永不过期”(控制变量法)



相关文章
|
11月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
925 0
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
507 0
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
238 14
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
4574 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
1927 2
|
消息中间件 设计模式 缓存
spring源码设计模式分析(四)-观察者模式
spring源码设计模式分析(四)-观察者模式
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
1056 5
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
|
XML 设计模式 Java
Spring-源码深入分析(一)
Spring-源码深入分析(一)

热门文章

最新文章