⽹站⾝份信息验证规则Cookie、Session、Token、JWT说明
什么是认证(Authentication)
通俗地讲就是验证当前⽤户的⾝份,证明“你是你⾃⼰”(⽐如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统⾥录⼊的指纹相匹配时,就打卡成功)
互联⽹中的认证:
⽤户名密码登录
邮箱发送登录链接
⼿机号接收验证码
只要你能收到邮箱/验证码,就默认你是账号的主⼈
什么是授权(Authorization)
⽤户授予第三⽅应⽤访问该⽤户某些资源的权限
你在安装⼿机应⽤的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限)
你在访问⼩程序时,当登录时,⼩程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个⼈信息)实现授权的⽅式有:cookie、session、token、OAuth
什么是凭证(Credentials)
实现认证和授权的前提
是需要⼀种媒介(证书)来标记访问者的⾝份
在战国时期,商鞅变法,发明了照⾝帖。照⾝帖由官府发放,是⼀块打磨光滑细密的⽵板,上⾯刻有持有⼈的头像和籍贯信息。
国⼈必须持有,如若没有就被认为是⿊户,或者间谍之类的。
字符串长度规则
在现实⽣活中,每个⼈都会有⼀张专属的居民⾝份证,是⽤于证明持有⼈⾝份的⼀种法定证件。通过⾝份证,我们可以办理⼿机卡/银⾏卡/个⼈贷款/交通出⾏等等,这就是认证的凭证。
在互联⽹应⽤中,⼀般⽹站(如掘⾦)会有两种模式,游客模式和登录模式。游客模式下,可以正常浏览⽹站上⾯的⽂章,⼀旦想要点赞/收藏/分享⽂章,就需要登录或者注册账号。当⽤户登录成功后,服务器会给该⽤户使⽤的浏览器颁发⼀个令牌
(token),这个令牌⽤来表明你的⾝份,每次浏览器发送请求时会带上这个令牌,就可以使⽤游客模式下⽆法使⽤的功能。
什么是 Cookie
HTTP 是⽆状态的协议(对于事务处理没有记忆能⼒,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独⽴的,服务端⽆法确认当前访问者的⾝份信息,⽆法分辨上⼀次的请求发送者和这⼀次的发送者是不是同⼀个⼈。所以服务器与浏览器为了进⾏会话跟踪(知道是谁在访问我),就必须主动的去维护⼀个状态,这个状态⽤于告知服务端前后两个请求是否来⾃同⼀浏览器。⽽这个状态需要通过 cookie 或者 session 去实现。
cookie 存储在客户端: cookie 是服务器发送到⽤户浏览器并保存在本地的⼀⼩块数据,它会在浏览器下次向同⼀服务器再发起请求时被携带并发送到服务器上。
cookie 是不可跨域的:每个 cookie 都会绑定单⼀的域名,⽆法在别的域名下获取使⽤,⼀级域名和⼆级域名之间是允许共享使⽤的(靠的是 domain)。
cookie 重要的属性
属性说明name=value键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型- 如果值为 Unic
ode 字符,需要为
字符编码。- 如果值为⼆进制数据,则需要使⽤ BASE64 编码。domain指定 cookie 所属域名,默认是当前域名path指定
cookie 在哪个路径(路由)下⽣效,默认是 '/'。如果设置为/abc,则只有/abc下的路由可以访问到该 cookie,
如:/abc/read。maxAgecookie 失效的时间,单位秒。如果为整数,则该 cookie 在 maxAge 秒后失效。如果为负数,该
cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie 。如果为 0,表⽰删除该 cookie 。默
认为 -1。-⽐ expires 好⽤。expires过期时间,在设置的某个时间点后该 cookie 就会失效。⼀般浏览器的 cookie 都是默认
储存的,当关闭浏览器结束这个会话的时候,这个 cookie 也就会被删除secure该 cookie 是否仅被使⽤安全协议传输。安
全协议有 HTTPS,SSL等,在⽹络上传输数据之前先将数据加密。默认为false。当 secure 值为 true 时,cookie 在 HTTP 中是⽆效,在 HTTPS 中才有效。httpOnly如果给某个 cookie 设置了 httpOnly 属性,则⽆法通过 JS 脚本读取到该 cookie
的信息,但还是能通过 Application 中⼿动修改 cookie,所以只是在⼀定程度上可以防⽌ XSS 攻击,不是绝对的安全
什么是 Session
session 是另⼀种记录服务器和客户端会话状态的机制
session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中
session 认证流程:
⽤户第⼀次请求服务器的时候,服务器根据⽤户提交的相关信息,创建对应的 Session
请求返回时将此 Session 的唯⼀标识信息 SessionID 返回给浏览器
浏览器接收到服务器返回的 SessionID 信息后,会将此信息存⼊到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
当⽤户第⼆次访问服务器的时候,请求会⾃动判断此域名下是否存在 Cookie 信息,如果存在⾃动将 Cookie 信息也发送给
服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查对应的 Session 信息,如果没有到说明⽤户没
有登录或者登录失效,如果到 Session 证明⽤户已经登录可执⾏后⾯操作。
根据以上流程可知,SessionID 是连接 Cookie 和 Session 的⼀道桥梁,⼤部分系统也是根据此原理来验证⽤户登录状态。Cookie 和 Session 的区别
安全性: Session ⽐ Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
存取值的类型不同:Cookie 只⽀持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
有效期不同: Cookie 可设置为长时间保持,⽐如我们经常使⽤的默认登录功能,Session ⼀般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
存储⼤⼩不同:单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远⾼于 Cookie,但是当访问量过多,会占⽤过多的服务器资源。
访问资源接⼝(API)时所需要的资源凭证
简单 token 的组成: uid(⽤户唯⼀的⾝份标识)、time(当前时间的时间戳)、sign(签名,token 的前⼏位以哈希算法压缩成的⼀定长度的⼗六进制字符串)
特点:
服务端⽆状态化、可扩展性好
⽀持移动端设备
安全
⽀持跨程序调⽤
token 的⾝份验证流程:
客户端使⽤⽤户名跟密码请求登录
服务端收到请求,去验证⽤户名与密码
验证成功后,服务端会签发⼀个 token 并把这个 token 发送给客户端
客户端收到 token 以后,会把它存储起来,⽐如放在 cookie ⾥或者 localStorage ⾥
客户端每次向服务端请求资源的时候需要带着服务端签发的 token
服务端收到请求,然后去验证客户端请求⾥⾯带着的 token ,如果验证成功,就向客户端返回请求的数据每⼀次请求都需要携带 token,需要把 token 放到 HTTP 的 Header ⾥
基于 token 的⽤户认证是⼀种服务端⽆状态的认证⽅式,服务端不⽤存放 token 数据。⽤解析 token 的计算时间换取
session 的存储空间,从⽽减轻服务器的压⼒,减少频繁的查询数据库
token 完全由应⽤管理,所以它可以避开同源策略
Refresh Token
另外⼀种 token——refresh token
refresh token 是专⽤于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要⽤户输⼊登录⽤户名与密码,会很⿇烦。有了 refresh token,可以减少这个⿇烦,客户端直接⽤ refresh token 去更新
access token,⽆需⽤户进⾏额外的操作。
Access Token 的有效期⽐较短,当 Acesss Token 由于过期⽽失效时,使⽤ Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,⽤户就只能重新登录了。
Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接⼝响应时间造成影响,也不需要向 Session ⼀样⼀直保持在内存中以应对⼤量的请求。
Token 和 Session 的区别
Session 是⼀种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。⽽ Token 是令牌,访问资源接⼝(API)时所需要的资源凭证。Token 使服务端⽆状态化,不会存储会话信息。
Session 和 Token 并不⽭盾,作为⾝份认证 Token 安全性⽐ Session 好,因为每⼀个请求都有签名还能防⽌监听以及重放攻击,⽽ Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存⼀些状态。
所谓 Session 认证只是简单的把 User 信息存储到 Session ⾥,因为 SessionID 的不可预测性,暂且认为是安全的。⽽ Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是认证和授权,认证是
针对⽤户,授权是针对 App 。其⽬的是让某App 有权利访问某⽤户的信息。这⾥的 Token 是唯⼀的。不可以转移到其它 App上,也不可以转到其它⽤户上。Session 只提供⼀种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站⽅,不应该共享给其它⽹站或者第三⽅ App。所以简单来说:如果你的⽤户数据可能需要和第三⽅共享,或者允许第三⽅调⽤ API 接⼝,⽤ Token 。如果永远只是⾃⼰的⽹站,⾃⼰的 App,⽤什么就⽆所谓了。
什么是 JWT
JSON Web Token(简称 JWT)是⽬前最流⾏的跨域认证解决⽅案。
是⼀种认证授权机制。
JWT 是为了在⽹络应⽤环境间传递声明⽽执⾏的⼀种基于 JSON 的开放标准(RFC 7519)。JWT 的声明⼀般被⽤来在⾝份提供者和服务提供者间传递被认证的⽤户⾝份信息,以便于从资源服务器获取资源。⽐如⽤在⽤户登录上。
可以使⽤ HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进⾏签名。因为数字签名的存在,这些传递的信息是可信的。
阮⼀峰⽼师的 JSON Web Token ⼊门教程讲的⾮常通俗易懂,这⾥就不再班门弄斧了
JWT 的原理
JWT 认证流程:
⽤户输⼊⽤户名/密码登录,服务端认证成功后,会返回给客户端⼀个 JWT
客户端将 token 保存到本地(通常使⽤ localstorage,也可以使⽤ cookie)
当⽤户希望访问⼀个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使⽤Bearer 模式添加 JWT,其内容看起来是下⾯这样
服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许⽤户的⾏为
因为 JWT 是⾃包含的(内部包含了⼀些会话信息),因此减少了需要查询数据库的需要
因为 JWT 并不使⽤ Cookie 的,所以你可以使⽤任何域名提供你的 API 服务⽽不需要担⼼跨域资源共享问题(CORS)
因为⽤户的状态不再存储在服务端的内存中,所以这是⼀种⽆状态的认证机制
JWT 的使⽤⽅式
客户端收到服务器返回的 JWT,可以储存在 Cookie ⾥⾯,也可以储存在 localStorage。
⽅式⼀
当⽤户希望访问⼀个受保护的路由或者资源的时候,可以把它放在 Cookie ⾥⾯⾃动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段⾥,使⽤ Bearer 模式添加 JWT。
GET /calendar/v1/events
Host: ample
Authorization: Bearer <token>
由于 JWT 是⾃包含的,因此减少了需要查询数据库的需要
JWT 的这些特性使得我们可以完全依赖其⽆状态的特性提供数据 API 服务,甚⾄是创建⼀个下载流服务。
因为 JWT 并不使⽤ Cookie ,所以你可以使⽤任何域名提供你的 API 服务⽽不需要担⼼跨域资源共享问题(CORS)⽅式⼆
跨域的时候,可以把 JWT 放在 POST 请求的数据体⾥。
⽅式三
通过 URL 传输
Token 和 JWT 的区别
相同:
都是访问资源的令牌
都可以记录⽤户的信息
都是使服务端⽆状态化
都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别:
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取⽤户信息,然后验证 Token 是否有效。
JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使⽤密钥解密进⾏校验(校验也是 JWT ⾃⼰实现的)即可,不需要查询或者减少查询数据库,因为 JWT ⾃包含了⽤户信息和加密的数据。
常见的前后端鉴权⽅式
Session-Cookie
Token 验证(包括 JWT,SSO)
OAuth2.0(开放授权)
常见的加密算法
1
哈希算法(Hash Algorithm)⼜称散列算法、散列函数、哈希函数,是⼀种从任何⼀种数据中创建⼩的数字“指纹”的⽅法。哈希算法将数据重新打乱混合,重新创建⼀个哈希值。
哈希算法主要⽤来保障数据真实性(即完整性),即发信⼈将原始消息和哈希值⼀起发送,收信⼈通过相同的哈希函数来校验原始数据是否真实。
哈希算法通常有以下⼏个特点:
2 的 128 次⽅为 340282366920938463463374607431768211456,也就是 10 的 39 次⽅级别
2 的 160 次⽅为 1.4615016373309029182036848327163e+48,也就是 10 的 48 次⽅级别
2 的 256 次⽅为 1.1579208923731619542357098500869 × 10 的 77 次⽅,也就是 10 的 77 次⽅
正像快速:原始数据可以快速计算出哈希值
逆向困难:通过哈希值基本不可能推导出原始数据
输⼊敏感:原始数据只要有⼀点变动,得到的哈希值差别很⼤
冲突避免:很难到不同的原始数据得到相同的哈希值,宇宙中原⼦数⼤约在 10 的 60 次⽅到 80 次⽅之间,所以 2 的
256 次⽅有⾜够的空间容纳所有的可能,算法好的情况下冲突碰撞的概率很低:
注意:
以上不能保证数据被恶意篡改,原始数据和哈希值都可能被恶意篡改,要保证不被篡改,可以使⽤RSA 公钥私钥⽅案,再配合哈希值。
哈希算法主要⽤来防⽌计算机传输过程中的错误,早期计算机通过前 7 位数据第 8 位奇偶校验码来保障(12.5% 的浪费效率低),对于⼀段数据或⽂件,通过哈希算法⽣成 128bit 或者 256bit 的哈希值,如果校验有问题就要求重传。
常见问题
使⽤ cookie 时需要考虑的问题
因为存储在客户端,容易被客户端篡改,使⽤前需要验证合法性
不要存储敏感数据,⽐如⽤户密码,账户余额
使⽤ httpOnly 在⼀定程度上提⾼安全性
尽量减少 cookie 的体积,能存储的数据量不能超过 4kb
设置正确的 domain 和 path,减少数据传输
cookie ⽆法跨域
⼀个浏览器针对⼀个⽹站最多存 20 个Cookie,浏览器⼀般只允许存放 300 个Cookie
移动端对 cookie 的⽀持不是很好,⽽ session 需要基于 cookie 实现,所以移动端常⽤的是 token 使⽤ session 时需要考虑的问题
将 session 存储在服务器⾥⾯,当⽤户同时在线量⽐较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
当⽹站采⽤集部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理⽤户请求的服务器不⼀定是那个创建 session 的服务器,那么该服务器就⽆法拿到之前已经放⼊到 session
中的登录凭证之类的信息了。
当多个应⽤要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应⽤可能部署的主机不⼀样,需要在各个应⽤做好 cookie 跨域的处理。
sessionId 是存储在 cookie 中的,假如浏览器禁⽌ cookie 或不⽀持 cookie 怎么办?⼀般会把 sessionId 跟在 url 参数后⾯即重写 url,所以 session 不⼀定⾮得需要靠 cookie 实现
移动端对 cookie 的⽀持不是很好,⽽ session 需要基于 cookie 实现,所以移动端常⽤的是 token 使⽤ token 时需要考虑的问题
如果你认为⽤数据库来存储 token 会导致查询时间太长,可以选择放在内存当中。⽐如 redis 很适合你对 token 查询的需
求。
移动端对 cookie 的⽀持不是很好,⽽ session 需要基于 cookie 实现,所以移动端常⽤的是 token
使⽤ JWT 时需要考虑的问题
因为 JWT 并不依赖 Cookie 的,所以你可以使⽤任何域名提供你的 API 服务⽽不需要担⼼跨域资源共享问题(CORS)
JWT 默认是不加密,但也是可以加密的。⽣成原始 Token 以后,可以⽤密钥再加密⼀次。
JWT 不加密的情况下,不能将秘密数据写⼊ JWT。
JWT 不仅可以⽤于认证,也可以⽤于交换信息。有效使⽤ JWT,可以降低服务器查询数据库的次数。
JWT 最⼤的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以⽅便扩展。但这也是 JWT 最⼤的缺点:由
于服务器不需要存储 Session 状态,因此使⽤过程中⽆法废弃某个 Token 或者更改 Token 的权限。也就是说⼀旦 JWT 签
发了,到期之前就会始终有效,除⾮服务器部署额外的逻辑。
JWT 本⾝包含了认证信息,⼀旦泄露,任何⼈都可以获得该令牌的所有权限。为了减少盗⽤,JWT的有效期应该设置得⽐
较短。对于⼀些⽐较重要的权限,使⽤时应该再次对⽤户进⾏认证。
JWT 适合⼀次性的命令认证,颁发⼀个有效期极短的 JWT,即使暴露了危险也很⼩,由于每次操作都会⽣成新的 JWT,
因此也没必要保存 JWT,真正实现⽆状态。
为了减少盗⽤,JWT 不应该使⽤ HTTP 协议明码传输,要使⽤ HTTPS 协议传输。
使⽤加密算法时需要考虑的问题
绝不要以明⽂存储密码
永远使⽤哈希算法来处理密码,绝不要使⽤ Base64 或其他编码⽅式来存储密码,这和以明⽂存储密码是⼀样的,使⽤哈
希,⽽不要使⽤编码。编码以及加密,都是双向的过程,⽽密码是保密的,应该只被它的所有者知道,这个过程必须是单
向的。哈希正是⽤于做这个的,从来没有解哈希这种说法,但是编码就存在解码,加密就存在解密。
绝不要使⽤弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 ,只使⽤强密码哈希算法。
绝不要以明⽂形式显⽰或发送密码,即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能,可以随机⽣成⼀
个新的⼀次性的(这点很重要)密码,然后把这个密码发送给⽤户。
分布式架构下 session 共享⽅案
1. session 复制
任何⼀个服务器上的 session 发⽣改变(增删改),该节点会把这个 session 的所有内容序列化,然后⼴播给所有其它节
点,不管其他服务器需不需要 session ,以此来保证 session 同步
优点:可容错,各个服务器间 session 能够实时响应。缺点:会对⽹络负荷造成⼀定压⼒,如果 session 量⼤的话可能会
造成⽹络堵塞,拖慢服务器性能。
2. 粘性 session /IP 绑定策略
采⽤ Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同⼀台服务器上,即将⽤户与服务器绑定。⽤户第⼀次请求
时,负载均衡器将⽤户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 session 的话,那么⽤户以后的每次请求
都会转发到 A 服务器上,相当于把⽤户和 A 服务器粘到了⼀块,这就是粘性 session 机制。
优点:简单,不需要对 session 做任何处理。缺点:缺乏容错性,如果当前访问的服务器发⽣故障,⽤户被转移到第⼆个
服务器上时,他的 session 信息都将失效。适⽤场景:发⽣故障对客户产⽣的影响较⼩;服务器发⽣故障是低概率事件。
实现⽅式:以 Nginx 为例,在 upstream 模块配置 ip_hash 属性即可实现粘性 session。
3. session 共享(常⽤)
使⽤分布式缓存⽅案⽐如 Memcached 、Redis 来缓存 session,但是要求 Memcached 或 Redis 必须是集
把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问⼀次 Redis ,但是这种⽅案带来的好处也是很⼤
的:
实现了 session 共享;
可以⽔平扩展(增加 Redis 服务器);
服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);
不仅可以跨服务器 session 共享,甚⾄可以跨平台(例如⽹页端和 APP 端)
4. session 持久化
将 session 存储到数据库中,保证 session 的持久化
优点:服务器出现问题,session 不会丢失缺点:如果⽹站的访问量很⼤,把 session 存储到数据库中,会对数据库造成
很⼤压⼒,还需要增加额外的开销维护数据库。
只要关闭浏览器,session 真的就消失了?
不对。对 session 来说,除⾮程序通知服务器删除⼀个 session,否则服务器会⼀直保留,程序⼀般都是在⽤户做 log off 的时候发个指令去删除 session。然⽽浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是⼤部分 session 机制都使⽤会话 cookie 来保存 session id,⽽关闭浏览器后这个 session id 就消失了,再次连接服务器时也就⽆法到原来的 session。如果服务器设置的 cookie 被保存在硬盘上,或者使⽤某种⼿段改写浏
览器发出的 HTTP 请求头,把原来的 session id 发送给服务器,则再次打开浏览器仍然能够打开原来的 session。恰恰是由于关闭浏览器不会导致 session 被删除,迫使服务器为 session 设置了⼀个失效时间,当距离客户端上⼀次使⽤ session 的时间超过这个失效时间时,服务器就认为客户端已经停⽌了活动,才会把 session 删除以节省存储空间。
Cookie、Session、Token、JWT

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。