最全面的SpringMVC教程(三)——跨域问题

news2025/2/27 8:35:46

前言

在这里插入图片描述

本文为 【SpringMVC教程】跨域问题 相关内容介绍。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同时,就会产生跨域。那么究竟什么是跨域,跨域问题该如何解决,本文具体将对同源策略什么是跨域简单请求与非简单请求跨域问题解决等进行详尽介绍~

📌博主主页:小新要变强 的主页
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

↩️本文上接:最全面的SpringMVC教程(二)——SpringMVC核心技术篇


目录

文章标题

  • 前言
  • 目录
  • 一、同源策略
  • 二、什么是跨域
  • 三、简单请求与非简单请求
  • 四、跨域问题解决
  • 后记

在这里插入图片描述


一、同源策略

同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。

二、什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同时,就会产生跨域。

在这里插入图片描述
举一个例子:从127.0.0.1:5000访问的页面中,有Javascript使用ajax访问127.0.0.1:8888的接口就会产生跨域。

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

非同源限制:

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求

三、简单请求与非简单请求

CORS全称是"跨域资源共享"(Cross-origin resource sharing);浏览器将CORS请求分成两类: 简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求:

  • (1) 请求方法是以下三种方法之一:HEAD;GET;POST
  • (2)HTTP的头信息不超出以下几种字段:Accept;Accept-Language;Content-Language;Last-Event-ID;Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)

这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。Ajax 的跨域设计就是,只要表单可以发,Ajax就可以直接发。

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

🍀(1)简单请求

基本流程

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.ydlclass.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequeston error回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。

(1)Access-Control-Allow-Origin

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

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

(4)withCredentials 属性

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在Ajax请求中打开withCredentials属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

xhr.withCredentials = false;

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

🍀(2)非简单请求

预检请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

下面是一段浏览器的JavaScript脚本。

var url = 'http://api.wang.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header

浏览器发现,这是一个非简单请求,就【自动】发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.ydlclass.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,"预检"请求的头信息包括两个特殊字段:

  • (1)Access-Control-Request-Method。该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
  • (2)Access-Control-Request-Headers。该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

预检请求的响应

服务器收到"预检"请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

 HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为’*',表示同意任意跨源请求。

Access-Control-Allow-Origin: *

如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

XMLHttpRequest cannot load http://api.wang.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

服务器回应的其他CORS相关字段如下:

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

(1)Access-Control-Allow-Methods

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

(2)Access-Control-Allow-Headers

如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

(3)Access-Control-Allow-Credentials

该字段与简单请求时的含义相同。

(4)Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

四、跨域问题解决

首先想到的就是使用过滤器进行统一的处理,当然在单个的servlet或者controller中也可以单独处理,基本的逻辑就是在响应的首部信息中加入需要的首部信息字段,解决方案如下:

public class CORSFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

对api为前缀的请求都进行处理:

<!-- CORS Filter -->
<filter>
    <filter-name>CORSFilter</filter-name>
    <filter-class>com.ydlclass.filter.CORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CORSFilter</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>

到这里,就可以简单的实现 CORS 跨域请求了,上面的过滤器将会为所有请求的响应加上Access-Control-Allow-*首部,换言之就是允许来自任意源的请求来访问该服务器上的资源。而在实际开发中可以根据需要开放跨域请求权限以及控制响应头部等等。

springmvc提供了更加简单的解决方案

在Controller 上使用 @CrossOrigin 注解就可以实现跨域,这个注解是一个类级别也是方法级别的注解:

@CrossOrigin(maxAge = 3600)
@RestController 
@RequestMapping("goods")
public class GoodsController{
}

如果同时在 Controller 和方法上都有使用@CrossOrigin 注解,那么在具体某个方法上的 CORS 属性将是两个注解属性合并的结果,如果属性的设置发生冲突,那么Controller 上的主机属性将被覆盖。

也可以使用配置类进行全局的配置:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("*")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(false).maxAge(3600);
    }
}

基于 XML 配置文件与上等效:

<mvc:cors>
  <mvc:mapping path="/api/**"
        allowed-origins="*"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="false"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="http://domain1.com" />

</mvc:cors>

后记

在这里插入图片描述
👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kvaser Leaf light HS v2 | 如何使用Excel发送和接收CAN报文数据

从1980年代&#xff0c;Kvaser就开始CAN产品的研发&#xff0c;在相关产品开发领域有近40多年的经验&#xff0c;对CAN和相关总线技术有着非常深入的研究。我们将分享一些有趣的发现和一些特定情况的技术处理&#xff0c;欢迎关注❤️广州智维电子科技有限公司❤️&#xff01;…

【微信小程序】冒泡事件与非冒泡事件、将文章数据从业务中分离、wxml的模块化

&#x1f3c6;今日学习目标&#xff1a;第十四期——冒泡事件与非冒泡事件、将文章数据从业务中分离、wxml的模块化 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人主页&#xff1a;颜颜yan_的个人主页 ⏰预计时间&#xff1a;30分钟 &#x1f389;专栏系列&#xff1a;我的第…

Day14--商品详情-渲染商品详情的数据并优化详情页的显示

提纲挈领&#xff1a; 那么如何在小程序中将这些html的字符串渲染成这莫好看的结构呢&#xff1f; 官方文档&#xff1a;【使用uni-ui组件库中的rich-text组件】 1.渲染商品详情信息 我的操作&#xff1a; 1》在页面结构中&#xff0c;使用 rich-text 组件&#xff0c;将带有…

盘点一个批量提取pdf文件目标信息的实用案例

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤你若盛开,清风自来。大家好&#xff0c;我是皮皮。一、前言前几天在帮助粉丝解决问题的时候&#xff0c;遇到一个简单的小需求&#xff0c;需要批量提取pdf文…

【零基础入门SpringMVC】第六期——尾声

一、注解配置SpringMVC 采用全注解开发&#xff0c;替代我们的web.xml和SpringMVC的核心配置文件 我们需要创建对应的配置类&#xff0c;继承AbstractAnnotationConfigDispatcherServletInitializer 使用的Servlet版本要求在3.0以上项目启动后容器会找到配置了&#xff0c;基于…

台积电跪舔美国,日本却醒悟了而选择独立发展芯片产业

近期台积电大举包机10架将精英人才和设备转往美国引发争议&#xff0c;然而这个时候日本却选择了独立发展芯片产业的道路&#xff0c;摆脱美国的限制&#xff0c;显然日本清醒地认识到依赖美国不会有好结果。台积电之前还在左右摇摆&#xff0c;希望既能继续获得美国芯片的订单…

测试用例的重要性,看完这篇就够了

测试用例对于测试工作的作用&#xff1a;1、指导测试的实施测试用例主要适用于集成测试、系统测试和回归测试。在实施测试时测试用例作为测试的标准&#xff0c;测试人员一定要按照测试用例严格按用例项目和测试步骤逐一实施测试。并对测试情况记录在测试用例管理软件中&#x…

干货 | 数字经济创新创业——如何发展绿色经济

下文整理自清华大学大数据能力提升项目能力提升模块课程“Innovation & Entrepreneurship for Digital Economy”&#xff08;数字经济创新创业课程)的精彩内容。主讲嘉宾&#xff1a;Kris Singh: CEO at SRII, Palo Alto, CaliforniaVisiting Professor of Tsinghua Unive…

[附源码]计算机毕业设计springboot房屋租赁系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【毕业设计】27-基于单片机的家庭监控及防盗报警_热释电报警_人体系统工程设计(原理图+源代码+仿真+实物照片+答辩论文)

【毕业设计】27-基于单片机的家庭监控及防盗报警/热释电报警/人体系统工程设计&#xff08;原理图源代码仿真实物照片论文&#xff09; 文章目录【毕业设计】27-基于单片机的家庭监控及防盗报警/热释电报警/人体系统工程设计&#xff08;原理图源代码仿真实物照片论文&#xff…

【Java实战】工作中规范使用Java集合

目录 一、前言 二、规范使用Java集合 1.【强制】关于 hashCode 和 equals 的处理&#xff0c;遵循如下规则&#xff1a; 2.【强制】判断所有集合内部的元素是否为空&#xff0c;使用 isEmpty() 方法&#xff0c;而不是 size() 0 的方式。 3.【强制】在使用 java.util.str…

接口自动化测试实践指导(中):接口测试场景有哪些

在第一篇文章中详细给小伙伴们讲解了接口自动化需要做哪些准备工作&#xff0c;准备工作中最后一步接口测试用例设计是非常重要的一个环节&#xff0c;用例设计的好不好&#xff0c;直接关系到我们的测试质量。那如何进行测试用例设计呢&#xff1f;这里呢我结合自身经验&#…

PYTHON 用几何布朗运动模型和蒙特卡罗MONTE CARLO随机过程模拟股票价格可视化分析耐克NKE股价时间序列数据...

原文链接&#xff1a;http://tecdat.cn/?p27099 金融资产/证券已使用多种技术进行建模。该项目的主要目标是使用几何布朗运动模型和蒙特卡罗模拟来模拟股票价格。该模型基于受乘性噪声影响的随机&#xff08;与确定性相反&#xff09;变量&#xff08;点击文末“阅读原文”获取…

【 医学影像| 数据预处理】

影像读取及预处理&#xff1a;预处理后的数据集建议保存在本地&#xff0c;可以减少训练时的部分资源消耗。里面提到了归一化的 对分割的一些理解&#xff1a;基于深度学习来做医学图像处理&#xff0c;主要的工作集中在了数据预处理部分&#xff1a;深入理解医学图像的格式和特…

GLAD:体全息

概述 自从伽伯1948年提出全息术后&#xff0c;光学全息术已经被广泛用于三维光学成像领域。体全息成像技术是采用体全息光栅作为成像元件对物体进行三维成像的技术。 1990年,由Barbastathis和Brady提出体全息成像技术&#xff0c;采用体全息光栅作为选择成像元件&#xf…

【微信小程序高频面试题——精选一】

微信小程序高频面试题小程序中如何进行接口请求&#xff1f;会不会跨域&#xff0c;为什么小程序的常用命令有哪些你认为微信小程序的优点是什么&#xff0c;缺点是什么微信小程序中的js和浏览器中的js以及node中的js的区别微信小程序中的数据渲染浏览器中有什么不同小程序中如…

全国所有地级市环境污染、企业、公路、固定资产、外商投资-最新面板数据

一、1990&#xff0d;2019年地级市面板数据 1、数据来源&#xff1a;中国城市统计年鉴、WIND数据库 2、时间跨度&#xff1a;2000-2019 3、区域范围&#xff1a;所有地级市 4、指标说明&#xff1a; 该份部分数据指标如下&#xff1a; 主营业务税金及附加(万元) 发明专利…

android-CHECK_xxx分析

android-CHECK_xxx 在android源码中有不少类似这样的用法&#xff0c;上图中就是检查获得的hal版本是否大于等于版本1_3&#xff0c;满足继续往下走&#xff0c;不满足则assert&#xff0c;并报错。 接下来就展开看看CHECK_xx家族&#xff1a; 用法 类型用法含义CHECK_EQ(val…

【SpringCloud】07 流量管理sentinel

sentinel Sentinel 是面向分布式服务架构的高可用流量防护组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 1. 微服务中的服务雪崩 服务雪崩效应是一种因“服务提供者的不可…

Springboot系列(二十二):如何纯文本转成.csv格式文件?|超级详细,建议收藏

一、前言&#x1f525; 不知道大家有咩有遇到这么个需求&#xff0c;给你一长串文本&#xff0c;要求你能导成excel格式展示数据&#xff0c;一时间我陷入了沉思&#xff0c;如果要常规转excel&#xff0c;最明显的一点就是固定表头名&#xff0c;然而并不是&#xff0c;这表头…