cookie、session和Token的区别?JWT又是什么?单点登录又是什么?头大?快进来看看,一文帮你捋清楚~

目录

0、HTTP是无状态的

1、前端标记cookie

1.1、cookie限制空间范围

1.2、cookie限制时间范围

1.3、cookie限制使用方式

2、服务端存储session库

2.1、我们先来简单聊聊session是什么?

2.2、session的存储方式

2.3、session的过期和销毁

2.4、session的分布式问题

2.5、session的缺点

3、token

3.1、理解token 

3.2、客户端存储token

3.3、token过期

3.4、token的编码

4、JWT

4.1、简单介绍JWT

4.2、JWT的组成

4.2.1、JWT头

4.2.2、有效载荷【用户信息】

4.2.3、签名哈希【防伪标志】

Base64URL算法:

4.3、JWT的用法

4.4.JWT优缺点

4.5、refresh token

4.6、session  token

5、单点登录

5.1、什么是单点登录?

5.2、“虚假”的单点登录(主域名相同)

5.3、“真实”的单点登录(主域名不同)

 标注:


0、HTTP是无状态的

        http协议是无状态的,也就是说http请求方和响应方无法维护状态,都是一次性的。但是在有的系统中,例如前面做过的OJ项目,都是刷题时,是需要用户登录后才能刷题。在这种情况下,我们是需要维护用户状态,否则用户就得不停地去登录登录,很影响用户体验感~

        那么,我们如何进行去维护用户的状态呢?

        标记!!!

        也就是说,我们可以给已经登录的用户进行一个标记。当后端登陆成功后,就给这个用户做一个标记,携带着这个标记返回到前端;前端获取到这个标记后,把这个标记存储起来;当前端后续再有请求发送给后端时,我们把这个标记带着;后端再次收到请求后,先验证这个标记是否合法,合法则进行后续逻辑操作,不合法就表示这个用户还未登陆。

        总结来说,就是:后端发标记——前端存标记——请求带标记

        具体落实下来,有以下几个方案:cookie+session、token


1、前端标记cookie

        前端存储,可以使用cookie、localStorage、sessionStorage等方式。如果采用cookie存储,cookie相比其他方式,借助http头、浏览器能力,cookie可以做到前端无感知。

        大致过程:

  • 在提供标记的接口(例如登录接口),通过HTTP返回头的Set-Cookie字段,直接存到浏览器上
  • 浏览器发起请求后,会自动把cookie通过HTTP请求头的Cookie字段,带给接口

1.1、cookie限制空间范围

        通过配置Domain / Path 来限制cookie的空间范围:Domain限制域,Path限制路径。

        Domain说明:Domain属性指定浏览器在发出HTTP请求时,哪些域名要附带这个cookie,如果没有指定这个属性,浏览器会默认是将其设为当前URL(存cookie的这个URL)的一级域名,例如www.baidu.com,就会设为baidu.com,并且如果访问baidu.com的任何子域名,HTTP请求也会带上这个cookie。并且如果服务器在Set-Cookie字段指定的域名不属于当前域名,浏览器会拒绝这个Cookie。也就是说,你自己本来的域名是aaa,你在Set-Cookie字段指定的域名是bbb,浏览器就会拒绝这个cookie。

        Path说明: Path属性指定浏览器发出HTTP请求时,

1.2、cookie限制时间范围

        通过配置Expires / Max-Age来限制cookie的时间范围~

        Expires说明:Expires属性指的是具体的到期时间,到了指定时间后,浏览器就不再保留这个cookie。这个值的格式为UTC格式~

        注:浏览器的时间是根据本地时间计算的,由于本地时间是不精确的,所以没有办法保证cookie一定会在服务器指定的时间过期~

        Max-Age说明:Max-Age属性,指的是这个cookie存在开始算起的秒数,秒数到了后,浏览器就不再存储这个cookie了

        注:上述两个属性同时设置了,Max-Age优先生效。

        注:如果不设置上述两个属性,或者将其设置为null,则表示cookie只在当前回话有效,浏览器关闭后,当前session结束,该cookie就会被删除~

1.3、cookie限制使用方式

        通过Secure / HttpOnly来限制cookie的使用方式~

        Secure说明:Secure属性指定浏览器只有在加密协议hTTPS下,才能将这个cookie发送给服务器。这个属性不需要我们自己来配置,他只是一个开关,如果当前协议是HTTP,浏览器就会默认关闭这个属性,如果是HTTPS就会自动打开这个开关~

        HttpOnly说明:HttpOnly属性也可以看作是一个开关,当我们把开关打开时,则指定该cookie无法通过js脚本(js中的document.cookie等操作)拿到这个值,防止cookie被脚本读到,也就是只有浏览器发出HTTP请求时,才会带上该cookie。


2、服务端存储session库

2.1、我们先来简单聊聊session是什么?

  1. session是用来记录用户状态的;
  2. session是由服务器端创建的;
  3. 同一个浏览器发起的多次请求,是同属于一次会话session;
  4. 首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId【唯一的】发送回客户端;
  5. 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话;

        上述的简单了解后,我们知道他其实已经自动将session的SessionId放到了cookie中存储,也就是说前端的cookie中,存储了SessionId。在实际项目中,我们在使用session时,一般会在服务器端存储session会话,session回话中,包含sessionID,用户信息等,在我们有新的请求发送时,我们就可以直接获取cookie中存储的SessionID,然后在服务器端存储的session回话中查找,看有没有sessionID和请求中带过来的ID一样的会话,例如登录相关,有这个会话则表示已登录,无则表示用户未登录~

session的登录验证流程:

说明:

  • 浏览器登录发送账号密码,服务端查用户库,校验用户
  • 服务端把用户登录状态存为 Session,生成一个 sessionId
  • 通过登录接口返回,把 sessionId set 到 cookie 上
  • 此后浏览器再请求业务接口,sessionId 随 cookie 带上
  • 服务端查 sessionId 校验 session
  • 成功后正常做业务处理,返回结果

2.2、session的存储方式

        前端只是存了一个sessionId,session的具体信息,还是需要我们自己存储一下滴~

存储方式:

  • Redis(推荐):内存型数据库,redis中文官方网站。以 key-value 的形式存,正合 sessionId-sessionData 的场景;且访问快;
  • 内存:直接放到变量里。一旦服务重启就没了;
  • 数据库:普通数据库,如MySQL。性能不高。

2.3、session的过期和销毁

        删除存储的session中的数据即可~

2.4、session的分布式问题

        通常服务端是集群,而用户请求过来会走一次负载均衡,不一定打到哪台机器上。那一旦用户后续接口请求到的机器和他登录请求的机器不一致,或者登录请求的机器宕机了,session 不就失效了吗?

这个问题现在有几种解决方式。

  • 一是从「存储」角度,把 session 集中存储。如果我们用独立的 Redis 或普通数据库,就可以把 session 都存到一个库里。
  • 二是从「分布」角度,让相同 IP 的请求在负载均衡时都打到同一台机器上。以 nginx 为例,可以配置 ip_hash 来实现。

        但通常还是采用第一种方式,因为第二种相当于阉割了负载均衡,且仍没有解决「用户请求的机器宕机」的问题。

2.5、session的缺点

        session的维护给服务器端会造成很大的困扰,我们必须找地方存放它,又要考虑分布式问题,甚至还要给他启用一套redis集群~

这个缺点,token就有效解决了~


3、token

3.1、理解token 

        例如在登录中,我们其实不需要往session中存太多东西,我们完全可以把要存的数据(轻量级数据)都打包到cookie中,这样一来,服务器就不同存了~ 而每次的请求来了,我们只需要验证cookie中的“证件”是否有效就可以了。

        上述这种方式就被叫做token。

token的大致工作流程:

说明:

  • 用户登录,服务端校验账号密码,获得用户信息
  • 把用户信息、token 配置编码成 token,通过 cookie set 到浏览器
  • 此后用户请求业务接口,通过 cookie 携带 token
  • 接口校验 token 有效性,进行正常业务接口处理

3.2、客户端存储token

使用cookie或localStorage或sessionStorage等...

3.3、token过期

        把过期时间和数据一起放过去,验证时判断就可以了

3.4、token的编码

        token的编码是base64编码,我们可以搜索base64,随便打开一个编码网站,如下:

        上述这种把某些信息放在一起,通过base64编码后,好像没什么用。我大可以把cookie中的token值复制出来,解码,然后修改为我想要的用户的信息,再进行编码,放到cookie中,发送给后端,是不是就可以获取其他的用户信息了呢?

        答案是肯定的。那我们应该怎么办呢?

        针对上述这种操作,我们需要防止对token的数据这个数据的篡改!

        如何防篡改?给token签名!!!

        具体的我们可以根据JWT来看看,看看JWT是如何生成token的~

4、JWT

4.1、简单介绍JWT

        JWT是JSON WEB TOKEN的缩写,它是基于RFC7519标准定义的一种可以安全传输读的JSON对象,由于使用了数字签名,所以是可信任和安全全的。

        之所以使用JWT,是因为Token是属于无状态的,每个人定制的规则不同,生成的的结果就会不同。所以目前,多数都是采用JWT为统一令牌标准~

4.2、JWT的组成

jwt实例可以在这个网站看看效果:JSON Web Tokens - jwt.io

例如,我们就按照网页上默认的点击生成看看:

我们可以看到,编码后的结果是有三个部分组成的,每个部分之间用 . 分割【上述中,各个部分对应的字体颜色是一致的】。三个部分的含义:

  1. jwt头信息
  2. 有效载荷【用户信息】
  3. 签名哈希【防伪标志】

4.2.1、JWT头

 JWT头部分是一个描述JWT元数据的JSON对象,通常如下:

{
  "alg": "HS256",
  "typ": "JWT"
}

说明:

  • alg:表示签名使用的算法,默认为HMAC SHA256(HS256)
  • typ:表示令牌的类型,JWT令牌统一写为JWT

最后,使用Base64URL算法将上述JSON对象转换为字符串保存

4.2.2、有效载荷【用户信息】

        这一部分是JWT主体内容部分,也是一个JSON对象,包含需要传递的数据。JWT指定七个默认的字段供选择:

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除了上述默认的字段外,我们还可以自定义私有字段,如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

        注:默认情况下,JWT是未加密的,也就是说其他人可以解读你的内容,所以不要构建隐私信息字段。

 最后,使用Base64URL算法将上述JSON对象转换为字符串保存

4.2.3、签名哈希【防伪标志】

        签名是对上面两部分数据签名。通过指定的算法生成哈希,以确保数据不会被篡改。

        首先,需要指定一个密钥(secret)。该密钥存在服务器中,不向用户公开。然后使用JWT头中指定的签名算法(HS256)根据下列公式,生成签名:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), 
secret)

        在计算出签名哈希后,JWT头、有效载荷、和签名哈希三个部分组成一个字符串,每个部分用“.”分隔,构成整个JWT对象

Base64URL算法:

        如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。 作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/“和”=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"=“去掉,”+“用”-“替换,”/“用”_"替换,这就是Base64URL算法。

4.3、JWT的用法

        客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。

        此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

4.4.JWT优缺点

  • JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
  • 生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
  • 存储在客户端,不占用服务端的内存资源
  • JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。
  • 当JWT未加密时,一些私密数据无法通过JWT传输。
  • JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
  • JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
  • 为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

4.5、refresh token

        业务接口用来鉴权的 token,我们称之为 access token。越是权限敏感的业务,我们越希望 access token 有效期足够短,以避免被盗用。但过短的有效期会造成 access token 经常过期,过期后怎么办呢?

        一种办法是,让用户重新登录获取新 token,显然不够友好,要知道有的 access token 过期时间可能只有几分钟。

        另外一种办法是,再来一个 token,一个专门生成 access token 的 token,我们称为 refresh token。

  • access token 用来访问业务接口,由于有效期足够短,盗用风险小,也可以使请求方式更宽松灵活
  • refresh token 用来获取 access token,有效期可以长一些,通过独立服务和严格的请求方式增加安全性;由于不常验证,也可以如前面的 session 一样处理

有了 refresh token 后,几种情况的请求流程变成这样:

如果 refresh token 也过期了,就只能重新登录了。

4.6、session  token

        session和token的边界还是很模糊的,就像上面4.5提到的,refresh token也可能是以session的形式组织维护的。

        狭义上,我们通常认为session是前端存在cookie上,数据存在服务器端;token是前端存在哪儿都行,数据存在token里。非要对比这两个,其实就是在对比前端存哪儿,数据存哪儿~


5、单点登录

5.1、什么是单点登录?

        业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」。

5.2、“虚假”的单点登录(主域名相同)

        简单的,如果业务系统都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好办了。可以直接把 cookie domain 设置为主域名 baidu.com,百度也就是这么干的。

5.3、“真实”的单点登录(主域名不同)

        比如滴滴公司,同时拥有didichuxing.com xiaojukeji.com didiglobal.com等域名,种 cookie 是完全绕不开的。

这要能实现「一次登录,全线通用」,才是真正的单点登录。

这种场景下,我们需要独立的认证服务(就是把登录业务单拎出来,放在一台服务器上),通常被称为 SSO(Single Sign On)。

一次从A系统引发登录,到B系统不用登录的完整流程:

说明:

  • 用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO
  • SSO 没登录过,也就没有 sso 系统下没有凭证(注意这个和前面 A ticket 是两回事),输入账号密码登录
  • SSO 账号密码验证成功,通过接口返回做两件事:一是种下 sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket
  • 客户端拿到 ticket,保存起来,带着请求系统 A 接口
  • 系统 A 校验 ticket,成功后正常处理业务请求
  • 此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO
  • SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket
  • 客户端拿到 ticket,保存起来,带着请求系统 B 接口

「完整版本:考虑浏览器的场景」

上面的过程看起来没问题,实际上很多 APP 等端上这样就够了。但在浏览器下不见得好用。

看这里:

        对浏览器来说,SSO 域下返回的数据要怎么存,才能在访问 A 的时候带上?浏览器对跨域有严格限制,cookie、localStorage 等方式都是有域限制的。

这就需要也只能由 A 提供 A 域下存储凭证的能力。一般我们是这么做的:

 

图中我们通过颜色把浏览器当前所处的域名标记出来。注意图中灰底文字说明部分的变化。

  • 在 SSO 域下,SSO 不是通过接口把 ticket 直接返回,而是通过一个带 code 的 URL 重定向到系统 A 的接口上,这个接口通常在 A 向 SSO 注册时约定
  • 浏览器被重定向到 A 域下,带着 code 访问了 A 的 callback 接口,callback 接口通过 code 换取 ticket
  • 这个 code 不同于 ticket,code 是一次性的,暴露在 URL 中,只为了传一下换 ticket,换完就失效
  • callback 接口拿到 ticket 后,在自己的域下 set cookie 成功
  • 在后续请求中,只需要把 cookie 中的 ticket 解析出来,去 SSO 验证就好
  • 访问 B 系统也是一样

 标注:

        本文是根据下文魔改过来的,加了一些个人的观点

        文章: 鉴权必须了解的5个兄弟:cookie、session、token、jwt、单点登录 - 知乎

好啦,本文结束咯,下期见

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519)。它是一种轻量级的安全传输方式,用于在网络应用间传递声明信息。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。 头部包含了关于令牌的元数据和加密算法的信息,通常由两部分组成:令牌类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。 载荷是JWT的主要内容,包含了一些声明信息,如用户ID、角色、权限等。载荷可以自定义,但建议只包含一些非敏感的信息,因为JWT是可解码的。 签名是对头部和载荷进行加密生成的,用于验证JWT的真实性和完整性。签名需要使用头部中指定的算法和密钥进行生成,接收方可以通过验证签名来确保JWT没有被篡改。 生成JWT token的过程如下: 1. 创建一个包含所需声明信息的JSON对象。 2. 使用Base64编码头部和载荷,形成两个字符串。 3. 将两个字符串用点号连接起来,形成一个未签名的JWT。 4. 使用指定的算法和密钥对未签名的JWT进行签名,生成签名字符串。 5. 将签名字符串添加到未签名的JWT末尾,形成最终的JWT token。 验证JWT token的过程如下: 1. 将接收到的JWT token按点号分割为头部、载荷和签名三部分。 2. 使用相同的算法和密钥对头部和载荷进行签名,生成一个新的签名字符串。 3. 将新生成的签名字符串与接收到的签名进行比较,如果相同,则说明JWT token是有效的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙洋静

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

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

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

打赏作者

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

抵扣说明:

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

余额充值