问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

如何使用JWT完成注销(退出登录)功能

创作时间:
作者:
@小白创作中心

如何使用JWT完成注销(退出登录)功能

引用
CSDN
1.
https://blog.csdn.net/qq_63358859/article/details/145369518

JSON Web Tokens(JWT)是一种无状态处理用户身份验证的方法。它帮助建立认证机制而不将身份验证状态存储在任何存储中,无论是会话内存还是数据库。相反,根据你选择的用户payload生成token,并在客户端的请求中使用它来标识服务器上的用户。

JSON Web Tokens(JWT)

JWT(JSON Web Tokens)是一种无状态处理用户身份验证的方法。这意味着什么?

JWT帮助建立认证机制而不将身份验证状态存储在任何存储中,无论是会话内存还是数据库。因此,当检查用户的身份验证状态时,不需要访问会话内存或执行数据库查询。相反,根据你选择的用户payload生成token,并在客户端的请求中使用它来标识服务器上的用户。

因此,基本上,每当创建token时,就可以永远使用它,或者直到它过期为止。JWT生成器可以在生成的时候有一个指定过期时间的选项。

不过,你想让已经生成的token失效该怎么办呢?当用户注销或者更改密码的时候你该做些什么呢?

注销

通常在使用JWT做身份验证时,客户端将token储存在某个地方,并将附加到每个需要身份验证的请求上。所以注销的第一件事就是删除储存在客户端上的token(例如浏览器本地储存)。在这种情况下,客户端没有了token,请求需要认证的接口自然会得到未认证的响应。

不过,这就够了吗?这个是防君子不防小人的做法,实际上在注销之前通过一些手段将token拿到手,在注销后依然可以使用!不信的的话可以自己尝试下。

让我们从后台注销token,你可能会说桥豆麻袋,这对于jwt来说并不是那么简单,你并不能像删除cookie和session那样来删除token。

实际上,JWT的目的与session不同,不可能强制删除或失效已经生成的token。

是的,Token可以设置过期。在生成token时可以指定过期时间,你可以在有效的payload中加上exp字段,就像这样:

exp字段为时间戳,这里的iat字段代表发布时间,此Token设置为在发布后5秒过期。如果你不想拥有永远有效的Token,你应该给你的JWT设置一个合理的到期时间,时间的长短取决于你的应用,我们将在这里使用时长为一天Token,并在登录操作中生成它们,对于NodeJS应用程序,代码应该如下所示:

const jwt = require('jsonwebtoken');
const token = jwt.sign(payload, 'your-secret', {expiresIn: '1d'});

当这个token过期时,验证器会返回一个error,而且后端一旦收到需要授权的请求,就会以未授权的响应状态进行响应。通常,你将从客户端删除令牌并将用户重定向到登录页面。因此,在这个例子中,所有用户在使用你的应用程序1天后将自动注销。

很酷,但我还是想注销!

如前所述,你不能在Token创建后手动使其过期,你实际上不能像使用session那样在服务器端使用JWT注销。或者,除非,你可以...

使用JWT应该是无状态的,这意味着你应该将所需的一切存储在payload中,并跳过对每个请求执行DB查询。但是,如果你计划有一个严格的注销功能,无法等待Token自动过期,即使你已经从客户端清除了Token,然后你可能需要违背无状态规则并执行一些查询。

有一种实现可能是,存储一个所谓的“token副本”所有Token是有效的,还没有到期,你可以挑选一个拥有TTL功能的数据库,TTL被用于为Token记录Token过期之前剩余的时间量。Redis是一个很好的选择,这将允许在内存中快速访问列表,然后,在某个中间件中,运行在每个授权请求上,你应该检查提供的令牌是否在名单中,如果不在的话就抛出一个未认证异常,如果有让它通过,JWT验证将处理它并确定它是否已过期或仍然有效。

退出时删除token:

const { authorization = "" } = ctx.request.header;
const token = authorization.replace("Bearer ", "");
const user = jwt.verify(token, JWT_SECRET);
let user_name = user?.user_name;
const tokenKey = `token:blacklist:${user_name}`;
await Redis.del(tokenKey);
console.error("Logout error:", error);

登陆存储token:

const { captchaKey, user_name } = ctx.request.body;
// 1. 获取用户信息(在token的payload中, 记录id, user_name, email)
// 从返回结果对象中剔除password属性, 将剩下的属性放到res对象
const { password, imgUrl, createdAt, updatedAt, ...res } = await getUerInfo({ user_name });
await Redis.del(captchaKey);
let token = "Bearer " + jwt.sign(res, JWT_SECRET, { expiresIn: "1d" });
const tokenKey = `token:blacklist:${user_name}`;
await Redis.set(tokenKey, token, 60 * 60 * 24);
// 生成token(加密内容, 加密密钥, options[expiresIn:过期时间])
console.error("用户登录失败", err);

中间件验证token:

const auth = async (ctx, next) => {
  const { authorization = "" } = ctx.request.header;
  const token = authorization.replace("Bearer ", "");
  const user = jwt.verify(token, JWT_SECRET);
  const blacklistKey = `token:blacklist:${user.user_name}`;
  const isBlacklisted = await Redis.get(blacklistKey);
  console.error("token已失效");
  ctx.app.emit("error", invalidToken, ctx);
  case "TokenExpiredError":
    console.error("token已过期", err);
    return ctx.app.emit("error", tokenExpiredError, ctx);
  case "JsonWebTokenError":
    console.error("无效的token", err);
    return ctx.app.emit("error", invalidToken, ctx);
}

结论

似乎,在使用JSON Web令牌时创建干净的注销流程并不那么简单,你应该让Token保持活动状态,直到它自己过期为止;或者,如果你希望在用户注销时限制Token的使用,则选择储存一个Token副本。总而言之,只需遵循以下4个要点:

  1. 设置令牌的合理过期时间
  2. 注销时从客户端删除存储的Token
  3. 拥有Token副本的数据库,这些Token仍有一些生存时间
  4. 针对每个请求根据副本查询授权情况

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号