Cookie Session Token的理解

1.HTTP无状态性
HTTP是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的。
2.Cookie+Session处理登入
Cookie 是服务器端发送给客户端的一段特殊信息,这些信息以文本的方式存放在客户端,客户端每次向服务器端发送请求时都会带上这些特殊信息。
当访问一个页面的时候,服务器在下行http报文中,命令浏览器存储一个字符串;浏览器再访问同一个域的时候,将把这个字符串携带到上行http请求中。
第一次访问第一个服务器,不可能携带cookie。必须是服务器得到这次请求,在下行响应报头中,携带cookie信息,此后每一次浏览器往这个服务器发出的请求,都会携带这个cookie。
有了 Cookie 之后,服务器端就能够获取到客户端传递过来的信息了,如果需要对信息进行验证,还需要通过 Session。
客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个便是 Session 对象。
有了 Cookie 和 Session 之后,我们就可以进行登录认证了。
像是我做的搜索引擎构建中,在对百科爬取时用的就是Cookie登入。
session在计算机网络应用中被称为“会话控制”。客户端浏览器访问网站的时候,服务器会向客户浏览器发送一个每个用户特有的会话编号sessionID,让他进入到cookie里,服务器同时也把sessionID和对应的用户信息、用户操作记录在服务器上,这些记录就是session。客户端浏览器再次访问时,会发送cookie给服务器,其中就包含sessionID。服务器从cookie里找到sessionID,再根据sessionID找到以前记录的用户信息就可以知道他之前操控些、访问过哪里。
用户首次登入时:
在这里插入图片描述

  1. 用户访问 a.com/pageA,并输入密码登录。
  2. 服务器验证密码无误后,会创建 SessionId,并将它保存起来。
  3. 服务器端响应这个 HTTP 请求,并通过 Set-Cookie 头信息,将 SessionId 写入 Cookie 中。
  4. 浏览器会根据Set-Cookie中的信息,自动将SessionId存储至cookie中。
    服务器端的 SessionId 可能存放在很多地方,例如:内存、文件、数据库等。我的商城项目中就是将Session放在Redis中。
    第一次登录完成之后,后续的访问就可以直接使用 Cookie 进行身份验证了:
    在这里插入图片描述

 

  1. 用户访问 a.com/pageB 页面时,会自动带上第一次登录时写入的 Cookie。
  2. 服务器端比对 Cookie 中的 SessionId 和保存在服务器端的 SessionId 是否一致。
  3. 如果一致,则身份验证成功。

虽然我们使用 Cookie + Session 的方式完成了登录验证,但仍然存在一些问题:

  • 由于服务器端需要对接大量的客户端,也就需要存放大量的 SessionId,这样会导致服务器压力过大。
  • 如果服务器端是一个集群,为了同步登录态,需要将 SessionId 同步到每一台机器上,无形中增加了服务器端维护成本。
  • 由于 SessionId 存放在 Cookie 中,所以无法避免 CSRF 攻击。CSRF攻击的原因是浏览器会自动带上cookie,而浏览器不会自动带上token。但是token不能防止xss攻击,xss:用户通过各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本获取信息,发起请求之类的操作。
    CSRF攻击:
    CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
    受害者登录a.com,并保留了登录凭证(Cookie)。
    攻击者引诱受害者访问了b.com。
    b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
    a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
    a.com以受害者的名义执行了act=xx。
    攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
    在这里插入图片描述

3.Token登入认证:

为了解决 Session + Cookie 机制暴露出的诸多问题,我们可以使用 Token 的登录方式。
Token是服务端生成的一串字符串,以作为客户端请求的一个令牌。当第一次登录后,服务器会生成一个 Token 并返回给客户端,客户端后续访问时,只需带上这个 Token 即可完成身份认证。
用户首次登录时:
在这里插入图片描述

  1. 用户输入账号密码,并点击登录。
  2. 服务器端验证账号密码无误,创建 Token。
  3. 服务器端将 Token 返回给客户端,由客户端自由保存。
    后续访问时:
    在这里插入图片描述
    在这里插入图片描述

 

  1. 用户访问 a.com/pageB 时,带上第一次登录时获取的 Token。
  2. 服务器端验证 Token ,有效则身份验证成功。
    Token 机制的特点:
  • 服务器端不需要存放 Token,所以不会对服务器端造成压力,即使是服务器集群,也不需要增加维护成本。
  • Token 可以存放在前端任何地方,可以不用保存在 Cookie 中,提升了页面的安全性。
  • Token 下发之后,只要在生效时间之内,就一直有效,如果服务器端想收回此 Token 的权限,并不容易。
    Token 的生成方式:
    最常见的 Token 生成方式是使用 JWT(Json Web Token),它是一种简洁的,自包含的方法用于通信双方之间以 JSON 对象的形式安全的传递信息。
    JWT token分成3部分:
    1.header:头部(header)
    2.payload:载荷(payload)
    3.signature:签证(signature)
    header:
    jwt的头部承载两部分信息:
    声明类型,这里是jwt
    声明加密的算法 通常直接使用 HMAC SHA256
    完整的头部就像下面这样的JSON:
{
    "typ": "JWT",
    "alg": "HS256"
}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
注意base64是对称加密,所以解密也很简单。
payload:
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
1.标准中注册的声明
2.公共的声明
3.私有的声明
比如定义一个payload:

{
    "name":"Free码农",
    "age":"28",
    "org":"今日头条"
}

然后将其进行base64加密,得到Jwt的第二部分:
eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9
多个业务服务器之间使用相同的 Token 对用户来说是不安全的。因为任何一个服务器拿到 Token 都可以仿冒用户去另一个服务器处理业务……悲剧随时可能发生。
为了防止这种情况发生,就需要在认证服务器产生 Token 的时候,把使用该 Token 的业务服务器的信息记录在 Token 中,这样当另一个业务服务器拿到这个 Token 的时候,发现它并不是自己应该验证的 Token,就可以直接拒绝。
不过只要服务端能确认是自己签发的 Token,而且其信息未被改动过,那就可以认为 Token 有效——“签名”可以作此保证。平时常说的签名都存在一方签发,另一方验证的情况,所以要使用非对称加密算法。但是在这里,签发和验证都是同一方,所以对称加密算法就能达到要求,而对称算法比非对称算法要快得多(可达数十倍差距)。
signature:
signature 部分为 JWT 的签名,主要为了让 JWT 不能被随意篡改,这个签证信息由3部分组成:
1 header (base64后的)
2 payload (base64后的)
3 secret
这个部分其实就是把:
base64加密后的header和
base64加密后的payload
使用点符号“.”连接起来组成字一个字符串,然后再通过在header中声明的加密方式进行加盐secret组合加密,这样就构成了JWT的第三部分:
49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY
具体:
输入 base64url 编码的 header 部分、 . 、base64url 编码的 playload 部分,输出 unsignedToken
输入服务器端私钥、unsignedToken,输出 signature 签名。

const base64Header = encodeBase64(header)
const base64Payload = encodeBase64(payload)
const unsignedToken = `${base64Header}.${base64Payload}`
const key = '服务器私钥'

最后token计算如下:

const base64Header = encodeBase64(header)
const base64Payload = encodeBase64(payload)
const base64Signature = encodeBase64(signature)
token = `${base64Header}.${base64Payload}.${base64Signature}` 
signature = HMAC(key, unsignedToken)

服务器在判断 Token 时:
通过

const [base64Header, base64Payload, base64Signature] = token.split('.')
const signature1 = decodeBase64(base64Signature)
const unsignedToken = `${base64Header}.${base64Payload}`
const signature2 = HMAC('服务器私钥', unsignedToken)
if(signature1 === signature2) {
  return '签名验证成功,token 没有被篡改'
}
const payload =  decodeBase64(base64Payload)
if(new Date() - payload.iat < 'token 有效期'){
  return 'token 有效'
} 

密钥secret是保存在服务端的,可以看做是服务器端私钥,服务端会根据这个密钥进行生成token,而且验证token合法性的时候也要用到它,所以一定要保护好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值