cookie、session和Token的区别?JWT又是什么?单点登录又是什么?

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是什么?

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

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

说明:

  • 浏览器登录发送账号密码,服务端查用户库,校验用户
  • 服务端把用户登录状态存为 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 系统也是一样
欢迎阅读!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇