一个请求从发出到返回,需要浏览器和服务端的协调配合。浏览器要把自己的请求参数带给服务端,服务端校验参数之后,除了返回数据,也可能会顺便把请求是否缓存,cookie等信息告诉浏览器。当请求是跨域请求的时候,这个过程还要复杂一些。接下来咱们就看看跨域会有什么问题,又需要前后端进行怎样的配合。

普通跨域

我有一个朋友,叫小王。前端小王和后端同事小马准备联调一个登录的api。假设是/login;小王在把登录账号和密码都准备好之后,愉快的发起了post提交。结果很意外,请求的响应被浏览器拦截了,浏览器还贴心的在console上抛出了一个错误。



小王翻译了一下,原来是被CORS策略拦截掉了。这个策略大概意思是说,服务端如果允许不同origin的请求,那就需要在返回的response header里面带上Access-Control-Allow-Origin这个header。否则浏览器在拿到响应并发现响应头里没有这个header时,就会把响应给吞掉,而不会交给js进行下一步处理。

小王把这个事情告诉了小马,然后小马在返回的header中加上了

Access-Control-Allow-Origin: *

现在小王终于可以拿到返回的结果了。

这里要注意,浏览器不是在请求阶段就对请求进行拦截,而是正常发出请求,拿到服务端的响应之后,开始查看响应header里面有没有Access-Control-Allow-Origin这个header,如果没有,响应的结果就不会到js那里去。

非简单请求的跨域

后来小王觉得在post中发送表单格式的body太麻烦,希望使用JSON格式的请求体提交。小马觉得就是几行代码的事,就同意了。但是小王改成JSON的消息体之后发现又被CORS拦截了,并抛出了下面的错误:

在上面的报错中,我们看到了 preflight 的单词。那这又是怎么回事呢?原来,修改请求体之后,这个跨域请求不再是简单请求了,需要在发起请求之前先进行 preflight 请求。那么什么是简单请求呢?

  • 请求方法包括GET, HEAD, POST
  • response header里面不能包含cors安全header以外的header。
  • Content-Type 只限于text/plain, multipart/form-data, application/x-www-form-urlencoded

由于json数据的content-type导致这个post请求不再是简单请求,而对于非简单请求,之前允许所有域名跨域访问是被禁止的。所以还是要修改Access-Control-Allow-Origin为特定的请求域名。在开发模式下,可能是http://localhost:3000之类的。

小马在重新修改Access-Control-Allow-Origin,小王又拿到了登录成功的结果。可以联调下一个api了。

带cookie的跨域

登录是基于session的,也就是说,登录成功后,server会通过set-cookie,将cookie设置到浏览器中,这样,下次访问同源下的api时,cookie就会被带上。

然而,奇怪的是,小王发现登录成功后,调用别的接口,cookie并没有被带上,导致server无法识别出用户信息,最终返回错误(状态码为401)。

withCredentials

原来,浏览器发起跨域请求的时候,是不会主动带上cookie的,如果一个请求需要cookie,需要开发者设置一个选项,以fetch api为例:

fetch('http://baidu.com:3000', {
// ...
credentials: true
})

如果使用xhr api来请求,则需要这样写:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/'; function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true; // 带上cookie
invocation.onreadystatechange = handler;
invocation.send();
}
}

小王在设置请求之后又发起了一次请求。却发现cookie还是没有带上去。小王只好在MDN继续查看资料,发现在set-cookie时需要带一个sameSite的属性。

sameSite

sameSite是为了防止csrf攻击而产生的属性,如果不知道啥是CSRF攻击,可以看我这篇文章。由于我们需要在请求中带上cookie,所以需要在set-cookie时将cookie的sameSite设置为none;又由于将sameSite设置为none时,也需要将Secure设置上,所以请求需要基于https;

小王最后一次请求小马对api进行了上诉更改,服务器终于认出请求来自谁,并返回了正确的结果,跨域的踩坑之旅算是告一段落。

总结

很多时候,我们可能只会关注请求体是什么,响应有没有正确返回,而忽略了header部分。殊不知,header在缓存,web安全,浏览器正确解析结果中发挥了重要的作用,比如本文中的一系列Access-Control-Allow-*的header。希望本文能对您理解跨域有所帮助。(本文完)

参考资料

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies#samesite_cookies

https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Authentication

cookie跨域那些事儿的更多相关文章

  1. 解决cookie跨域访问

    一.前言 随着项目模块越来越多,很多模块现在都是独立部署.模块之间的交流有时可能会通过cookie来完成.比如说门户和应用,分别部署在不同的机器或者web容器中,假如用户登陆之后会在浏览器客户端写入c ...

  2. cookie 跨域访问的解决方案

    Cookie 同域单点登录  最近在做一个单点登录的系统整合项目,之前我们使用控件实现单点登录(以后可以介绍一下).但现在为了满足客户需求,在不使用控件情况下实现单点登录,先来介绍一下单点登录.    ...

  3. asp.net关于Cookie跨域(域名)的问题

    Cookie是一个伟大的发明,它允许Web开发者保留他们的用户的登录状态.但是当你的站点有一个以上的域名时就会出现问题了.在Cookie规范上 说,一个cookie只能用于一个域名,不能够发给其它的域 ...

  4. 基于Cookie跨域的单点登录问题

    由于项目中,需要用的单点登录,主要的思路是:系统1:用户名密码-->写入Cookie-->其他系统读取Cookie. 1.在同一个服务器下的Cookie共享 @Component(&quo ...

  5. golang-在gin中cookie跨域设置(配合ajax)

    1.当我在golang中,在前后端分离的情况下使用cookies时发现,跨域没有被允许.代码如下: func AccessJsMiddleware() gin.HandlerFunc { return ...

  6. nginx处理cookie跨域

    今天在部署公司项目的时候碰到一个问题 项目地址是xxx.xx.xx.122:7480 项目A后台请求地址为xxx.xx.xx.123:8080/data-sso 开始nginx配置是 server { ...

  7. 解决前后端分离后的Cookie跨域问题

    一. 前端Ajax关键配置 $.ajax({ type: "post", url: xxx, data: xxx, contentType: 'application/json', ...

  8. cookie 跨域解决方法

    1.Nginx 正向和反向代理的区别 正向代理和反向代理的区别:正向代理隐藏真实客户端,反向代理隐藏真实服务端,图示: 2.cookie跨域问题 因为cookie存在跨域问题,其中一个解决方法是,设置 ...

  9. Iframe和Frame中实现cookie跨域的方法(转载)

    在Iframe和Frame中默认是不支持Cookie跨域的,但通过设置P3P协议相关的响应头可以解决这一问题.关于p3p协议: P3P: Platform for Privacy Preference ...

随机推荐

  1. 正则表达式 test 踩坑指南

    正则表达式 test 踩坑指南 test 只能使用一次,第二次返回的是错误结果! reg = /edg|edge/g; /edg|edge/g reg.test(`edg`) true reg.tes ...

  2. UTC 时间转换 All In One

    UTC 时间转换 All In One http://www.timebie.com/cn/stduniversal.php UTC 时间 世界的每个地区都有自己的本地时间,在 Internet 及无 ...

  3. js 动态修改页面文本字体

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. DAPHNE PATEL:有主见的人,才能活出精彩人生

    有主见的人,会活出什么样子呢?近日,NGK灵石团队技术副总裁DAPHNE 女士给出了答案. DAPHNE PATEL表示,有主见的人,才能活出精彩的人生.为什么这么说呢? DAPHNE PATEL用自 ...

  5. 源码分析:CyclicBarrier 之循环栅栏

    简介 CyclicBarrier 是一个同步辅助工具,允许一组线程全部等待彼此达到共同屏障点,且等待的线程被释放后还可以重新使用,所以叫做Cyclic(循环的). 应用场景 比如出去旅行时,导游需要等 ...

  6. Java并发包源码学习系列:同步组件CyclicBarrier源码解析

    目录 CyclicBarrier概述 案例学习 类图结构及重要字段 内部类Generation及相关方法 void reset() void breakBarrier() void nextGener ...

  7. scala函数至简原则是什么?

    1.return可以省略,Scala会使用函数体的最后一行代码作为返回值 2.如果函数体只有一行代码,可以省略花括号 3.返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略) 4.如果有 ...

  8. CSS 书写禅机

    这是未来的趋势所向,如是我行. 注意:原文发表于 2017-9-6,随着框架不断演进,部分内容可能已不适用. CSS 日渐惹人憎恶. 究其原因颇多,归根结底,皆因 CSS 给人的感觉总是飘渺迷蒙.变幻 ...

  9. RabbitMQ(三) SpringBoot2.x 集成 RabbitMQ

    3-1 RabbitMQ 整合 SpringBoot2.x 生产者发送消息 创建 SpringBoot 项目application.properties 配置 spring.rabbitmq.host ...

  10. 【转】理解Serverless

    理解Serverless No silver bullet. - The Mythical Man-Month 许多年前,我们开发的软件还是C/S(客户端/服务器)和MVC(模型-试图-控制器)的形式 ...