前端网络基础 - Token

本文详细探讨了Session认证机制的问题,包括内存占用、分布式部署的挑战以及频繁数据库查询。接着介绍了Token令牌认证机制,以军队指挥为例解释其原理,并分析了JsonWebToken(JWT)的构成和安全性。通过node第三方模块jsonwebtoken,展示了如何生成和验证JWT。最后,讨论了Token的优缺点,包括强制失效和续签策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

Session认证机制存在的问题

Token令牌

Token令牌认证的特点

Json Web Token

node第三方模块jsonwebtoken的使用

sign方法

verify方法

verify校验异常情况

基于jsonwebtoken模块完成Token身份认证

实现步骤

实现代码

Token身份认证的优缺点

Token强制失效的方案

Token续签问题

Token续签实现方案


Session认证机制存在的问题

  1. Session是基于Cookie工作的,所以当浏览器禁用cookie,或者发生跨站请求时,Session就无法工作了。
  2. Session如果存储在服务器内存中,则会占用大量服务器内存,并且当项目是分布式部署到多个服务器时,多个服务器内存中session的互相共享与同步成为一个问题。
  3. Session如果存储的数据库中,则可以解决分布式部署多个服务器间session共享和同步问题,但是如果数据库挂了,则所有分布式服务器的session认证都会失败,所以数据库也要集群部署,这意味着一份session需要被复制到多个数据库中。
  4. Session认证机制,本质是基于用户名和密码的认证机制,服务器需要取出session中保存的用户名和密码,去和数据库中的用户名和密码进行对比,对比一致才能认证成功,而这意味着每次session认证都需要进行一次数据库查询。

问题1:sessionid可以选择保存在浏览器的webStorage中,具体操作方案是,服务器将sessionid放在自定义响应头(非Set-Cookie)中,或者放在响应体中,浏览器收到响应后,从自定义响应头或响应体中取出sessionid,保存到浏览器webStorage中。当浏览器再次请求服务器时,需要手动从浏览器webStorage中取出sessionid加入自定义请求头(非Cookie)或请求体中,服务器收到请求后,从自定义请求头或请求体中解析出sessionid。

对于问题3:目前已有成熟的session数据库集群管理方案,但是任需要服务端提供大量硬盘进行session存储

对于问题4,主要需要优化的是:为了频繁重复的验证用户名和密码而去查询数据库,但是这是Session认证机制的安全性保障,无法优化。

可以分析出:Session认证机制会消耗服务端大量硬盘资源,以及会进行频繁重复的数据库查询。

这是Session认证机制的缺点。

Token令牌

在古代影视剧中,你会看到主帅在调兵遣将时,会拿起桌上的令牌给对应的将领,比如三国演义火烧博望坡一节中,诸葛亮依次给了张飞,赵云,关羽令牌,而张飞,赵云,关羽拿到令牌后就可以调动军队了。我们默认军队只听从主帅诸葛亮的调遣,也就是说军队需要认证调用者的身份为诸葛亮才能听命。那么拿着令牌的关羽为何也能调用军队呢?

其实很好理解,令牌是一种授权,它可以授予持有者某种权限,比如令牌赋予关羽调用军队的权限。

所以军队支持两种认证机制:1、主帅认证  2、令牌认证

为什么需要令牌认证呢?假设只有主帅认证,则关羽,张飞,赵云想要调动军队,则都需要带上诸葛亮,但是诸葛亮只有一个,所以诸葛亮会很忙,军队调用效率也不高。

诸葛亮很聪明,引入了令牌机制,并且和军队灌输了“见令牌如主帅亲临”,所以军队看到了令牌就像看到了主帅,也会服从持有者调遣。

而这种古老的认证机制也被设计者们引入到了网络身份认证中。

上面例子中,存在如下要素并且可以类比为:

  • 诸葛亮:服务器端注册过的用户名和密码
  • 军队:服务器端接口,该接口支持主帅认证和令牌认证
  • 关羽:调用接口方式

并且存在如下场景:

  • 关羽带诸葛亮调动军队:带上用户名密码调用接口,接口认证用户名和密码
  • 关羽带着令牌调用军队:带上令牌调用接口,接口认证令牌

Token令牌认证的特点

令牌中包含用户名,“见令牌如主帅亲临”

令牌中不包含密码,“令牌不是主帅本人”

所以服务器拿到令牌无法解析出密码,也就无法基于用户名和密码进行身份认证。

令牌认证,其实就是基于令牌本身进行认证。也就是令牌既要支持表明身份,也要支持防伪和自验证。

Json Web Token

JWT是一种成熟的token令牌生成方案,JWT生成的token令牌既能包含用户身份信息,也具有很好的防伪和自验证能力。

JWtoken 由三部分组成 header,payload,signature,三部分之间用"."连接。

header是一个经过base64URL编码的JSON对象,未编码前如下

{
  "alg": "HS256", // 签名算法
  "typ": "JWT" // token类型
}

header用于设置当前token的类型,一般是“JWT”,以及jwt的signature部分的加密算法。

payload也是一个经过base64URL编码的JSON对象,未编码前如下

{
  "name": "John Doe", // 用户身份
  "exp": 1516239022 // jwt失效时间
}

payload主要用于设置用户身份数据,以及jwt的失效时间。

signature是基于header和payload的签名数据,计算公式如下

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

其中HMACSHA256是根据header.alg选择的加密算法,secret是用户自定义的加密密钥。

signature的作用是防止jwt中header和payload被篡改,因为基于被篡改的header和payload,使用相同的加密算法和加密密钥是无法生成相同的jwt中signartue部分的。signarture既是jwt防伪的保障,也是jwt进行自验证的关键。 

另外需要注意的是base64URL编码并不是一种加密技术,它很容易被解码,所以在payload中我们最好不要添加用户隐私数据,比如密码。

node第三方模块jsonwebtoken的使用

jsonwebtoken是基于nodejs平台开发一个模块,基于该模块可以生成jwt,以及验证解析jwt。

jsonwebtoken模块对外暴露一个对象,该对象下主要有两个异步方法:sign和verify

sign方法

sign方法用于生成一个jwt字符串,该方法接收四个参数,依次如下:

  • payload:接收一个对象,用于传入用户身份信息
  • secret:接收一个字符串,作为jwt.signature的加密密钥
  • options:配置对象,介绍如下
  • callback(err, token):生成jwt结束时执行的回调函数,遵循nodejs的错误优先原则,第一个参数是生成jwt过程抛出的异常信息,第二个参数是成功生成的jwt字符串值。

options配置对象具有如下配置属性

algorithm签名加密算法,默认为HS256
expiresInjwt失效时间,默认单位为ms
notBeforejwt生效时间,默认单位为ms
audience受众
issuer签发人
jwtid编号
subject主题
noTimestamp
header
keyid
mutatePayload

 比较重要的是expiresIn,用于设定jwt的失效时间。

sign方法的返回值就是sign方法生成的jwt字符串。

但是需要注意的是,一旦指定了sign方法的第四个参数回调函数,则sign方法变为异步方法,jwt字符串只能通过回调函数获取。

如果不指定sign方法的第四个参数回调函数,则sign方法为同步方法,jwt字符串可以通过sign方法返回值获取。

所以sign方法的第四个入参callback本质不是用来获取jwt字符串,而是指定jwt字符串的后续处理逻辑,为了防止处理逻辑阻塞主流程,所以才将callback变为异步执行。

verify方法

verify方法用于校验和解析jwt字符串,该方法入参四个参数:

  • token:jwt字符串
  • secret:加密密钥
  • options:配置对象
  • callback(err, decode):回调函数,遵循nodejs错误优先原则,第一个参数是异常信息,第二个参数是根据jwt解码出来的数据

options配置对象具有如下配置属性

algorithms签名加密算法,默认为HS256
audience受众
complete默认为false,表示只返回payload解密数据,若为true表示返回{ payload, header, signature }解密数据
issuer签发人
ignoreExpirationif true do not validate the expiration of the token.
ignoreNotBeforeif true do not validate the not before of the token.
subject主题
clockTolerancenumber of seconds to tolerate when checking the nbf and exp claims, to deal with small clock differences among different servers
maxAgethe maximum allowed age for tokens to still be valid.
clockTimestampthe time in seconds that should be used as the current time for all necessary comparisons.
nonceif you want to check nonce claim, provide a string value here. It is used on Open ID for the ID Tokens. 

需要注意其中complete属性,该属性为true,则根据jwt字符串解码出来完整数据,即包含header,payload,signature,否则只包含payload

verify方法返回值也是jwt字符串解码出来的数据。

另外一旦verify传入callback,verify方法不再同步返回解码数据。

但是verify入参callback并不是异步执行的,而是同步执行的。

verify校验异常情况

NotBeforeError

该异常发生在jwt尚未生效时使用

TokenExpiredError

该异常发生在jwt字符串校验成功,但是已失效

JsonWebTokenError

该异常发生在jwt字符串校验失败

基于jsonwebtoken模块完成Token身份认证

实现步骤

  1. 登录接口验证用户名密码成功,服务器基于jsonwebtoken生成jwt字符串,作为cookie响应头返回
  2. 浏览器自动保存cookie:jwt,当再次请求服务器时自动携带,服务器解析出cookie:jwt进行校验解析

实现代码

nodejs平台基于jsonwebtoken的登录认证-Node.js文档类资源-优快云文库

登录成功,则生成jwt,并在jwt中加入用户身份信息name,并作为cookie响应给浏览器

浏览器携带cookie:jwt请求服务器,则服务器对jwt进行校验解析,若校验成功无异常,则说明jwt有效正确,然后从jwt解码出用户身份信息name。

上面jwt token是放在cookie中,这样在浏览器禁用cookie或跨站请求时,jwt token就会失效,所以我们可以将jwt token存储到浏览器端webStorage中。

Token身份认证的优缺点

优点

  • 服务器无需保存用户登录状态,节省了服务器内存和硬盘资源,以及节省了服务端对于登录状态的管理
  • 服务器端无需基于用户名密码验证,省去了频繁重复的查询数据库的动作

缺点

服务器一旦签发token,则在token有效期时间内,token始终有效,服务器端无法强制让token失效。

这将导致一个问题:即使用户已经退出登录,理论上应该让token失效,但是实际上,服务器端无法让还在有效期内的token失效,token的失效只能等它有效期过了。

原因是:token本质是一段支持自验证的字符串,而自验证的规则已经固化了,服务器端只能按照固化的验证规则对token进行验证,所以一个未失效的token总是可以通过固定规则的验证。

前面代码案例中,用户退出登录的实现都是清除了浏览器端保存的token信息,本质上并不是让token失效,如果用户将token刻意复制下来,然后再导入浏览器中,依旧可以实现免登录。

Token强制失效的方案

1、在服务器端建立黑名单,保存强制失效的token,这样服务器就可以通过查询黑名单来禁止强制失效token正常工作了。

该方案违背了token的初衷,token初衷是实现服务器端无状态保存,现在虽然不保存登录状态,但是却要保存黑名单token,依旧会给集群服务器带来管理压力。

2、减少token有效期的长度,降为20分钟到30分钟,这样可以减少token强制失效后的存活时间,降低影响。

该方案可以就是五十步笑百步的典型,依旧存在很大安全风险。

目前来看,没有太好的让Token强制失效的方案。

Token续签问题

前面让token强制失效,考虑的是token有效时长未用完,用户就退出登录,造成有效token可能被黑客盗用的风险。

但是还有一个实际生活中常见的问题:token有效时长用完了,用户还在继续正常访问网站,这时候会造成一个尴尬的问题,用户并没有退出登录,但是却要重新登录。

所以token续签成为一个需要思考的问题。

Token续签实现方案

服务器给用户发放两个token,分别是access_token和refresh_token。

access_token就是验证用户身份信息的token,它的有效期设置为短时间的,比如20分钟。

refresh_token用于更新access_token,即当access_token失效后,服务器可以验证refresh_token来后生成一个新的access_token。所以refresh_token的有效期一般设置为长时间跨度的,比如半个月。

需要注意的是refresh_token的secret和access_token的secret不能是同一个,否则refresh_token就可以被用来登录了。

基于acess_token和refresh_token实现token续签-Node.js文档类资源-优快云文库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员阿甘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值