WEB跨域请求无法传递(携带)第三方Cookie的问题剖析(你一定不知道的SameSite)

一、前言

最近在实际开发过程中遇到一个关于跨域请求无法传递Cookie的问题。该场景大致为:我们在一个web页面a下(假如该页面的网址为a.csdn.com),然后再改页面中使用ajax请求跨域访问了一个服务器b(假如其域名为b.baidu.comm)的接口。这就是一个普通的跨域请求,在这个跨域请求中,我们期望页面a在请求服务器b的接口的时候,浏览器能够将服务器b下的cookie(baidu.com下的cookie)都携带上。这样服务器b再收到请求之后能够从cookie中获取一些C端用的信息,如pin等。大致图示如下。

我们都知道这种跨域请求默认是不会成功的,我们需要做如下两件事情,才能够确保跨域请求成功地同时携带上baidu.com下的Cookie。

1、需要在服务端B开启CORS,这样页面a的跨域请求才能够被正常处理。

2、需要web页面a在请求的参数中添加withCredentials=true属性,这样浏览器才会携带cookie到服务端B。

然而事实却不是这样,通过上述操作之后,当我们在跨域请的时候,请求能够正常达到服务端B,服务端B返回的数据浏览器端也能够正常处理。但是跨域请求的过程中浏览器没有携带Cookie到服务端B,导致部分基于C端用户信息的逻辑出错。

看到这里我也很纳闷,按照之前的经验通过上述两步处理之后。服务端B是可能能够拿到Cookie的。那么为什么这里没有传递Cookie去服务端呢?通过一些列Google操作,我终于找到了具体原因。接下来就给大家详细分析下具体原因。

二、根因分析

通过一些列Google之后,终于找到了为什么上述跨域场景下CORS和withCredentials=true都设置的场景下,Cookie仍然不传递的原因。

这是因为Google去年在Chrome开始支持Cookie的新属性SameSite(该属性目前已经得到了业界各类大众浏览器的支持)。该属性提出的目的是为了解决接口Cookie被滥用,以及CSRF的问题(PS:在SameSite推出之前,上述跨域场景确实可以正常携带Cookie到b.baidu.com中)。

接下来我就为大家大致介绍一下什么SameSite属性。

1、SameSite属性简介

SameSite是Cookie的一个属性,其和path,domain一样,是一个普通的Cookie属性。其作用是限制该Cookie的在请求的时候是否被传递到Cookie所属服务的场景。

它一共有三个值,其基本含义分别如下:

Strict

Strict最为严格,完全禁止第三方 Cookie。即在跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

设置方式为:Set-Cookie: CookieName=CookieValue; SameSite=Strict;

Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外( top-level navigations)。

所有浏览器的Cookie默认SameSite属性都为Lax,即SameSite设置为空(未设置),则表示为Lax。

设置方式为:Set-Cookie: CookieName=CookieValue; SameSite=Lax;

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

设置了Strict或Lax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。

None

如果设置为None(前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效)。此时就表示Cookie不受约束在任何跨域场景下,只要设置了withCredentials=true都会发送到三方服务端。

设置方式为:Set-Cookie: CookieName=CookieValue; SameSite=None; Secure;

2、如何设置SameSite

大家看到上面介绍SameSite属性的时候,细心的同学应该注意到一个问题,SameSite属性的设置方式我们都是介绍的通过Set-Cookie这个Header来实现的。那么为什么我们不能通过Java中的Cookie对象来添加呢?

原因是因为SameSite是一个新兴的属性,而我们当前主流的JDK版本(jdk7和jdk8)从年代来讲都是比较老的代码了。因此其并不能够从接口层面支持新兴的SameSite属性。于是我们都只能够通过Set-Cookie的方式来来实现(样例如下)。

3、上述场景为什么无法携带Cookie

但我们认识了SameSite属性之后,我们自然而然就能够想到上述场景不携带Cookie的原因多半是因为SameSite被设置为了Lax或者Strict。于是我们查看浏览器中服务端B所在域名对应的Cookie发现,其确实所有Cookie都没有设置SameSite属性(如下图)。根据浏览器的默认策略,我们知道没有设置就表示SameSite=Lax。因此在ajax场景下SameSite=Lax的Cookie是无法进行传递的。

所以我们上述场景下无法传递Cookie到服务端。

三、解决办法

由于SameSite属性的支持导致了我们原来可以传递Cookie的场景都无法传递Cookie了。那么我们现在需要如何才能解决这个问题呢?这里我们给大家提供了如下三种方法。

1、取消跨域

这种方式解决起来比较简单,即将我们上述服务器B的域名修改为b.csdn.com。这样就不存在跨域的问题,也就轻松的解决了跨域问题。这种办法只适合服务端的域名可有有自己控制的场景了。

2、修改SameSite属性

这个方式也比较简单,我们只需要让Cookie生成方将将自己的需要的Cookie的SameSite属性设置为None即可。这样就可以让浏览器在任何跨域场景都发送该Cookie。

3、修改跨域请求方式

这种方式比较简单一点,且比较好由调用方操作。我们只需要将原来的ajax请求转换为如下三种请求类型(前提是业务上我们可以这样做)。

 

四、惯例

如果你对本文有任何疑问或者高见,欢迎添加公众号lifeofcoder共同交流探讨(添加公众号可以获得楼主最新博文推送以及”Java高级架构“上10G视频和图文资料哦)。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跳小闹成长记-跳爸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值