Cors跨域(一):深入理解跨域请求概念及其根因(上)

本文涉及的产品
.cn 域名,1个 12个月
简介: Cors跨域(一):深入理解跨域请求概念及其根因(上)

前言


你好,我是YourBatman。


做Web开发的小伙伴对“跨域”定并不陌生,像狗皮膏药一样粘着几乎每位同学,对它可谓既爱又恨。跨域请求之于创业、小型公司来讲是个头疼的问题,因为这类企业还未沉淀出一套行之有效的、统一的解决方案。


让人担忧的是,据我了解不少程序员同学(不乏有高级开发)碰到跨域问题大都一头雾水:


image.png


然后很自然的 用谷歌去百度一下搜索答案,但相关文章可能参差不齐、鱼龙混杂。短则半天长则一天(包含改代码、部署等流程)此问题才得以解决,一个“小小跨域”问题成功偷走你的宝贵时间。


既然跨域是个如此常见(特别是当下前后端分离的开发模式),因此深入理解CORS变得就异常的重要了(反倒前端工程师不用太了解),因此早在2019年我刚开始写博客那会就有过较为详细的系列文章:


image.png


本文提纲


image.png



版本约定

  • JDK:8
  • Servlet:4.x


正文


文章遵循一贯的风格,本文将采用概念 + 代码示例的方式,层层递进的进行展开叙述。那么上菜,先来个示例预览,模拟一下跨域请求,后面的一些的概念示例将以此作为抓手。


模拟跨域请求


要模拟跨域请求的根本是需要两个源:让请求的来源和目标源不一样。这里我就使用IDEA作为静态Web服务器(63342),Tomcat作为后端动态Servlet服务器(8080)。


说明:服务器都在本机,端口不一样即可


前端代码


index.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS跨域请求</title>
    <!--导入Jquery-->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
<button id="btn">跨域从服务端获取内容</button>
<div id="content"></div>
<script>
    $("#btn").click(function () {
        // 跨域请求
        $.get("http://localhost:8080/cors", function (result) {
            $("#content").append(result).append("<br/>");
        });
        // 同域请求
        $.get("http://localhost:63342");
        $.post("http://localhost:63342");
    });
</script>
</body>
</html>



使用IDEA作为静态web服务器,浏览器输入地址即可访问(注:端口号为63342):


image.png


后端代码


后端写个Servlet来接收cors请求


/**
 * 在此处添加备注信息
 *
 * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
 * @site https://yourbatman.cn
 * @date 2021/6/9 10:36
 * @since 0.0.1
 */
@Slf4j
@WebServlet(urlPatterns = "/cors")
public class CorsServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        String method = req.getMethod();
        String originHeader = req.getHeader("Origin");
        log.info("收到请求:{},方法:{}, Origin头:{}", requestURI, method, originHeader);
        resp.getWriter().write("hello cors...");
    }
}


启动后端服务器,点击页面上的按钮,结果如下:


image.png


服务端控制台输出:

... INFO  c.y.cors.servlet.CorsServlet - 收到请求:/cors,方法:GET, Origin头:http://loc



服务端输出日志,说明即使前端的Http Status是error,但服务端还是收到并处理了这个请求的


下面以此代码示例为基础,普及一下和Cors跨域资源共享相关的概念。


Host、Referer、Origin的区别


这哥三看起来很是相似,下面对概念作出区分。


image.png


  • Host:去哪里。域名+端口。值为客户端将要访问的远程主机,浏览器在发送Http请求时会带有此Header
  • Referer:来自哪里。协议+域名+端口+路径+参数。当前请求的来源页面的地址,服务端一般使用 Referer 首部识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等
  • 常见应用场景:百度的搜索广告就会分析Referer来判断打开站点是从百度搜索跳转的,还是直接URL输入地址的
  • 一般情况下浏览器会带有此Header,但这些case不会带有Referer这个头
  • 来源页面协议为File或者Data URI(如页面从本地打开的)
  • 来源页面是Https,而目标URL是http
  • 浏览器地址栏直接输入网址访问,或者通过浏览器的书签直接访问
  • 使用JS的location.href跳转
  • Origin:来自哪里(跨域)。协议+域名+端口。它用于Cors请求和同域POST请求


可以看到Referer与Origin功能相似,前者一般用于统计和阻止盗链,后者用于CORS请求。 但是还是有几点不同:


  1. 只有跨域请求,或者同域时发送post请求,才会携带Origin请求头;而Referer只要浏览器能获取到都会携带(除了上面说明的几种case外)


image.png


image.png


image.png


image.png


2.若浏览器不能获取到请求源页面地址(如上面的几种case),Referer头不会发送,但Origin依旧会发送,只是值是null而已(注:虽然值为null,但此请求依旧属于Cors请求哦),如下图所示:


image.png



image.png


Origin的值只包括协议、域名和端口,而Rerferer不但包括协议、域名、端口还包括路径,参数,注意不包括hash值


浏览器的同源策略


浏览器的职责是展示/渲染document、css、script脚本等,但是这些资源(将document、css、script统一称为资源)可能来自不同的地方,如本地、远程服务器、甚至黑客的服务器…浏览器作为万维网的入口,是我们接入互联网最重要的软件之一(甚至没有之一),因此它的安全性显得尤为重要,这就出现了浏览器的同源策略。


同源策略是浏览器一个重要的安全策略,它用于限制一个origin源的document或者它加载的脚本如何能与另一个origin源的资源进行交互。它能帮助阻隔恶意文档,减少(并不是杜绝)可能被攻击的媒介。


方便和安全往往是相悖的:安全性增高了,方便性就会有所降低


那么问题来了,什么才算同源?


同源的定义


URL被称作:统一资源定位符,同源是针对URL而言的。一个完整的URL各部分如下图所示:


image.png


Tips:域名和host是等同的概念,域名+端口号 = host+端口号(大部分情况下你看到域名并没有端口号,那是采用了默认端口号80而已)


同源:只和上图的前两部分(protocol + domain)有关,规则为:全部相同则为同源。这个定义不难理解,但有几点需要再强调一下:


  • 两部分必须完全一样才算同源
  • 这里的domain包含port端口号,所以总共是两部分而非三部分
  • 当然也有说三部分的(协议+host+port),理解其含义就成


下面通过举例来彻底了解下。譬如,我的源URL为:http://www.baidu.com/api/user,下面表格描述了不同URL的各类情况:


image.png


不同源的网络访问


浏览器同源策略的存在,限制了不同源之间的交互,实为不便。但是浏览器也开了一些“绿灯”,让其不受同源策略的约束。此种情况一般可分为如下三类:


1.跨域写操作(Cross-origin writes):一般是被允许的。如链接(如a标签)、重定向以及表单提交(如form表单的提交)


2.跨域资源嵌入(Cross-origin embedding):一般是允许的。比如下面这些例子:

  1. <script src="..."></script>标签嵌入js脚本
  2. <link rel="stylesheet" href="...">标签嵌入CSS
  3. <img>展示的图片
  4. <video>和<audio>媒体资源
  5. <object>、 <embed> 、<applet>嵌入的插件
  6. CSS中使用@font-face引入字体
  7. 通过<iframe>载入资源


3.跨域读操作(Cross-origin reads):一般是不被允许的。比如我们的http接口请求等都属于此范畴,也是本专栏关注的焦点


简单总结成一句话:浏览器自己是可以发起跨域请求的(比如a标签、img标签、form表单等),但是Javascript是不能去跨域获取资源(如ajax)。


如何允许不同源的网络访问


上面说到的第三种情况:跨域读操作一般是不允许跨域访问的,而这种情况是我们开发过程中最关心、最常见的case,因此必须解决。


Tips:这里的读指的是广义上的读,指的是从服务器获取资源(有response)的都叫读操作,而和具体是什么Http Method无关。换句话讲,所有的Http API接口请求都在这里都指的是读操作


可以使用 CORS 来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。

相关文章
|
6天前
|
JSON 安全 前端开发
浅析CORS跨域漏洞与JSONP劫持
浅析CORS跨域漏洞与JSONP劫持
21 3
|
27天前
|
安全
CORS 跨域资源共享的实现原理
CORS 跨域资源共享的实现原理
|
2月前
|
Web App开发 JSON 数据格式
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
|
2月前
|
API
【Azure Function】Function本地调试时遇见跨域问题(blocked by CORS policy)
【Azure Function】Function本地调试时遇见跨域问题(blocked by CORS policy)
【Azure Function】Function本地调试时遇见跨域问题(blocked by CORS policy)
|
2月前
|
安全 开发者 UED
|
2月前
|
前端开发 JavaScript
【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
|
2月前
|
JSON 小程序 API
【Azure API 管理】APIM CORS策略设置后,跨域请求成功和失败的Header对比实验
【Azure API 管理】APIM CORS策略设置后,跨域请求成功和失败的Header对比实验
|
2月前
|
前端开发 应用服务中间件 API
"揭秘!面试官必问:你是如何巧妙绕过跨域难题的?前端代理VS服务器端CORS,哪个才是你的秘密武器?"
【8月更文挑战第21天】在软件开发中,尤其前后端分离架构下,跨域资源共享(CORS)是常见的挑战。主要解决方案有两种:一是服务器端配置CORS策略,通过设置响应头控制跨域访问权限,无需改动前端代码,增强安全性;二是前端代理转发,如使用Nginx或Webpack DevServer在开发环境中转发请求绕过同源策略,简化开发流程但不适用于生产环境。生产环境下应采用服务器端CORS策略以确保安全稳定。
34 0
|
4月前
|
前端开发 安全 JavaScript
Spring Boot2 系列教程(十四)CORS 解决跨域问题
Spring Boot2 系列教程(十四)CORS 解决跨域问题
|
5月前
|
存储 安全 前端开发
第五章 跨域资源共享(CORS):现代Web开发中的关键机制
第五章 跨域资源共享(CORS):现代Web开发中的关键机制
158 1