首页 > 其他分享 >前端跨域问题

前端跨域问题

时间:2023-06-16 19:22:49浏览次数:24  
标签:Control Access 跨域 前端 问题 域名 Cookie 请求

跨域问题

什么是跨域?

  1. 什么是同源策略及其限制内容?

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS、CSRF 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

<img src=XXX>

<link href=XXX>

<script src=XXX>

  1. 常见跨域场景
    当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。

特别说明两点:

  • 第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。
  • 第二:在跨域问题上,仅仅是通过“URL 的首部”来识别而不会根据域名对应的 IP 地址是否相同来判断。“URL 的首部”可以理解为“协议, 域名和端口必须匹配”。

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

跨域有哪些方案?

这里只介绍几种开发中用的比较多的,几乎用不到的比如:

  • document.domain + iframe:适用主域名相同,子域名不同的跨域场景
  • window.name + iframe:利用 name 值最长可以 2M ,并用不同页面或不同域名加载后依然存在的特性
  • location.hash + iframe:适用通过 C 页面来实现 A 页面与 B 页面通信的场景
  1. CORS
    CORS 通信过程都是浏览器自动完成,需要浏览器(都支持)和服务器都支持,所以关键在只要服务器支持,就可以跨域通信,CORS 请求分两类,简单请求和非简单请求

另外 CORS 请求默认不包含 Cookie 以及 HTTP 认证信息,如果需要包含 Cookie,需要满足几个条件:

  • 服务器指定了 Access-Control-Allow-Credentials: true
  • 开发者须在请求中打开 withCredentials 属性: xhr.withCredentials = true
  • Access-Control-Allow-Origin 不要设为星号,指定明确的与请求网页一致的域名,这样就不会把其他域名的 Cookie 上传

简单请求:

需要同时满足两个条件,就属于简单请求:

  • 请求方法是:HEAD、GET、POST,三者之一
  • 请求头信息不超过以下几个字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-Id
    • Content-Type:值为三者之一 application/x-www/form/urlencoded、multipart/form-data、text/plain

需要这些条件是为了兼容表单,因为历史上表单一直可以跨域

浏览器直接发出 CORS 请求,具体来说就是在头信息中增加 Origin 字段,表示请求来源来自哪个域(协议+域名+端口),服务器根据这个值决定是否同意请求。如果同意,返回的响应会多出以下响应头信息

Access-Control-Allow-Origin: http://juejin.com // 和 Orign 一致 这个字段是必须的
Access-Control-Allow-Credentials: true // 表示是否允许发送 Cookie 这个字段是可选的
Access-Control-Expose-Headers: FooBar // 指定返回其他字段的值 这个字段是可选的
Content-Type: text/html; charset=utf-8 // 表示文档类型

在简单请求中服务器至少需要设置:Access-Control-Allow-Origin 字段

非简单请求

比如 PUT 或 DELETE 请求,或 Content-Type 为 application/json ,就是非简单请求。

非简单 CORS 请求,正式请求前会发一次 OPTIONS 类型的查询请求,称为预检请求,询问服务器是否支持网页所在域名的请求,以及可以使用哪些头信息字段。只有收到肯定的答复,才会发起正式 XMLHttpRequest 请求,否则报错

预检请求的方法是 OPTIONS,它的头信息中有几个字段

  • Origin: 表示请求来自哪个域,这个字段是必须的
  • Access-Control-Request-Method:列出 CORS 请求会用到哪些 HTTP 方法,这个字段是必须的
  • Access-Control-Request-Headers: 指定 CORS 请求会额外发送的头信息字段,用逗号隔开
  • OPTIONS 请求次数过多也会损耗性能,所以要尽量减少 OPTIONS 请求,可以让服务器在请求返回头部添加

Access-Control-Max-Age: Number // 数字 单位是秒

表示预检请求的返回结果可以被缓存多久,在这个时间范围内再请求就不需要预检了。不过这个缓存只对完全一样的 URL 才会生效

  1. Nginx 代理跨域

配置一个代理服务器向服务器请求,再将数据返回给客户端,实质和 CORS 跨域原理一样,需要配置请求响应头 Access-Control-Allow-Origin 等字段

server {
  listen 81; server_name www.domain1.com;
  location / {
    proxy_pass http://xxxx1:8080; // 反向代理
    proxy_cookie_domain www.xxxx1.com www.xxxx2.com; // 修改 cookie 里域名
    index index.html index.htm;
    // 当用 webpack-dev-server 等中间件代理接口访问 nignx 时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
    add_header Access-Control-Allow-Origin http://www.xxxx2.com; // 当前端只跨域不带 cookie 时,可为\*
    add_header Access-Control-Allow-Credentials true;
  }
}
  1. Node 中间件代理跨域

在 Vue 中 vue.config.js 中配置

module.export = {
   ...
   devServer: {
      proxy: {
         [ process.env.VUE_APP_BASE_API ]: {
            target: \'http://xxxx\',//代理跨域目标接口
            ws: true,
            changeOrigin: true,
            pathRewrite: {
               [ \'^\' + process.env.VUE_APP_BASE_API ] : \'\'
            }
         }
      }
   }
}

Node + express

const express = require(\'express\')
const proxy = require(\'http-proxy-middleware\')
const app = express()
app.use(\'/\', proxy({
   // 代理跨域目标接口
   target: \'http://xxxx:8080\',
   changeOrigin: true,
   // 修改响应头信息,实现跨域并允许带 cookie
   onProxyRes: function(proxyRes, req, res) {
      res.header(\'Access-Control-Allow-Origin\', \'http://xxxx\')
      res.header(\'Access-Control-Allow-Credentials\', \'true\')
   },
   // 修改响应信息中的 cookie 域名
   cookieDomainRewrite: \'www.domain1.com\' // 可以为 false,表示不修改
}));
app.listen(3000);
  1. WebSocket

WebSocket 是 HTML5 标准中的一种通信协议,以 ws://(非加密)和 wss://(加密)作为协议前缀,该协议不实行同源政策,只要服务器支持就行

因为 WebSocket 请求头信息中有 Origin 字段,表示请求源来自哪个域,服务器可以根据这个字段判断是否允许本次通信,如果在白名单内,就可以通信

  1. postMessage

postMessage 是 HTML5 标准中的 API,它可以给我们解决如下问题:

  • 页面和新打开的窗口间数据传递
  • 多窗口之间数据传递
  • 页面与嵌套的 iframe 之间数据传递
  • 上面三个场景之间的跨域传递

postMessage 接受两个参数,用法如下:

  • 参数一:发送的数据
  • 参数二:你要发送给谁就写谁的地址(协议 + 域名 +端口),也可以设置为*,表示任意窗口,为/表示与当前窗口同源的窗口
  1. JSONP

原理就是通过添加一个<script>标签,向服务器请求 JSON 数据,这样不受同源政策限制。服务器收到请求后,将数据放在一个 callback 回调函数中传回来。比如 axios。

不过只支持 GET 请求且不安全,可能遇到 XSS 攻击,不过它的好处是可以向老浏览器或不支持 CORS 的网站请求数据

    let script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = 'http://juejin.com/xxx?callback=handleCallback'
    document.body.appendChild(script)

    function handleCallback(res){
        console.log(res)
    }

服务器返回并立即执行

handleCallback({ code: 200, msg: 'success', data: [] })

指的就是对第三方使用 Cookie 的设置,在 Cookie 信息中添加 SameSite 属性

Set-Cookie: widget_session=123456; SameSite=None; Secure

SameSite 有三个值:

  • strict:严格模式,完全禁止使用 Cookie
  • lax:宽松模式,允许部分情况使用 Cookie,跨域的都行,a 标签跳转,link 标签,GET 提交的表单
  • none:任何情况下都会发送 Cookie,但必须同时设置 Secure 属性,意思是需要安全上下文,Cookie 只能通过 https 发送,否则无效

Chrome 80 之前默认值是 none,之后是 lax

不过在最新的 Chrome91 版本中这个已经被移除了,所以在 91 之前的版本依然可以使用

如果 Chrome 或 Edge 版本大于 91 小于 94 的话,可以通过 Chromium 支持的 command-line flag:

  • 右键 Chrome 或 Edge 浏览器,选择属性
  • 在目标(Target)属性末尾加上
    --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure
    

并且官方说的到 94 版本会连 comman-line 也会移除

官方的说法是任由开发者控制这两个选项,容易被攻击

标签:Control,Access,跨域,前端,问题,域名,Cookie,请求
From: https://www.cnblogs.com/yolocatcat/p/17486356.html

相关文章

  • 【React工作记录一百零八】前端小知识点扫盲笔记记录9
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结如何截取字符串<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge">......
  • 【React工作记录一百零九】前端小知识点扫盲笔记记录10
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结对称数<!DOCTYPEhtml><htmllang="en"> <head> <metacharset="UTF-8"/> <metahttp-equiv="X-UA-Compatible"content="IE=edge"/>......
  • blender 解决圆形 模型精度问题
    1、在右侧选中mesh 2、进入到编辑模式 3、左下角选择修改器 4、选择 修改器里 表面细分 5、下面两个按钮可以看左侧的实时效果 6、在物体模式下点击应用 ......
  • 仿喜茶GO小程序前端模板源码,奶茶店微信小程序源码
    本项目包含:首页点单喜茶百货百货详情历史订单我的积分商城积分商城详情页我的-微信一键登录我的-成为星球会员我的-个人资料我的-钱包我的-阿喜有礼会员码任务中心下载地址点击下载仿喜茶小程序源码运行效果图 ......
  • 如何构建前端日历组件
    前端日历组件是在网页中展示时间的重要组件之一,通常被应用于各种类别的网站和应用程序中。这篇文章将介绍如何构建一个高质量、易用且功能强大的前端日历组件。首先,我们需要确定需求和目标。一个好的日历组件应该具有以下特点:易于使用:用户应该可以轻松地使用日历组件并进行导......
  • Latex生成pdf过程中遇到cannot run的问题怎么处理?
    如:Latex生成pdf过程中遇到cannotrun的问题怎么处理?提示在生成pdf过程中出现了问题。这说明使用的pdf浏览器地址有问题为了后面修改代码过程中不会出现代码修改了pdf没变的情况,建议直接添加sumatraPDF阅读器直接到官网下载安装包即可。查看准确的安装位置右键->属性 在Late......
  • centOS下解决java生成图片中文乱码问题
    今天测试项目上线,其中有这样一个功能点:上传ppt转为图片。但是却出现了乱码,刚开始怀疑是系统编码的问题,但是:echo$LANG发现是utf-8,好像没什么问题,继续查找程序中的日志信息,发现从ppt中取出来的内容没有乱码,查看代码发现是生成图片时使用宋体字,猜测是字体没有安装。于是通过:fc-l......
  • Linux中-bash: /dev/null: Permission denied问题解决
    云上架构2021年08月06日09:19 ·  阅读682​今天在Centos7上运行如下命令 shell复制代码######添加hdfs用户#####useraddhdfs######切换至hdfs用户#####su-hdfs报如下错误 javascript复制代码-bash:/dev/null:Permissiondenied-bash......
  • Android_Uiautomatorviewer闪退问题,已解决
    现象1、cmd运行   (1)cdsdk的tools文件夹下   (2)./uiautomatorviewer2、提示错误信息-Djava.ext.dirs=/Users/env/android-sdk/tools/libisnotsupported.Use-classpathinstead.Error:CouldnotcreatetheJavaVirtualMachine.Error:Afatalexceptionh......
  • 前端学习C语言 - 数组和字节序
    数组本篇主要介绍:一维二维数组、字符数组、数组名和初始化注意点以及字节序。一维数组初始化有以下几种方式对数组初始化://定义一个有5个元素的数组,未初始化inta[5];//定义一个有5个元素的数组,将第一个初始化0,后面几个元素默认初始化为0inta[5]={0};//定义一个......