likes
comments
collection
share

写给前端的JSON Web Tokens(JWT)

作者站长头像
站长
· 阅读数 31

什么是JWT

JWT(JSON Web Tokens)是一种基于JSON的开放标准,用于在网络应用环境间安全地传输信息。核心特点是自包含,所有必要信息都编码在JWT内部。特别适合用于身份验证和授权场景。

为什么使用JWT

  1. 安全性:JWT的签名保证了令牌的完整性和真实性,确保信息不会在传输过程中被篡改或伪造。同时,由于JWT是基于标准的,可以使用加密算法来保护敏感信息,确保令牌只能被可信的接收方解密。
  2. 扩展性:由于JWT允许在Payload中添加自定义的声明,因此可以在令牌中携带更多的用户信息和相关权限,满足不同应用的需求。
  3. 跨平台和语言:JWT是基于JSON的开放标准,可以在不同的编程语言和平台间传递。
  4. 状态无关性:传统的会话认证在服务端需要保存用户的会话状态,而JWT是无状态的,所有信息都被包含在令牌本身中,这使得服务端不需要保存任何状态信息,从而降低了服务端的负担。

JWT组成结构

JWT由三部分组成,通过.分割

  • 头部(Header)
    • 包含关于JWT的元数据,如其类型(通常是JWT)和所使用的签名算法(如HS256RS256等)。
    • 这部分信息会被Base64Url编码形成JWT的第一个部分。
  • 载荷(Payload)
    • 载荷包含了JWT所携带的实际信息,如用户ID、用户角色、过期时间等。
    • 载荷可以分为三类:预定义(Registered)声明、公共(Public)声明和私有(Private)声明。
    • 这部分也会被Base64Url编码,形成JWT的第二个部分。
  • 签名(Signature):签名是通过将Header和Payload两部分拼接后,使用Header中指定的算法和一个密钥(secret)或公钥/私钥对计算得出的。用于验证JWT的完整性和确保其未被篡改。

示例如下 写给前端的JSON Web Tokens(JWT)

使用JWT的登录流程

如下图所示,在客户端登录时,服务端会根据必要信息生成并返回JWT,客户端收到后将其存储在cookielocalStorage中,在后续的请求中携带传给服务端。服务端对JWT进行验证,验证通过后对请求作出响应。

写给前端的JSON Web Tokens(JWT)

使用jsonwebtoken登录【可参考】

在Node.js环境下,使用三方库node-jsonwebtoken实现登录功能和验证jwt。

使用jwt.sign()生成token,使用jwt.verify()验证token正确性

// 登录
exports.login = catchAsync(async (req, res, next) => {
 const { email, password } = req.body;
  // 1) 验证是否发送邮箱、密码;
  if (!email || !password) {
    return next(new AppError('Please provide email and password!', 400));
  }
  // 2) 根据邮箱密码查询用户是否存在,密码是否正确;
  const user = await User.findOne({ email }).select('+password');
  if (!user) {
    return next(new AppError('There in no user with this email address', 404));
  }
  const correct = await user.correctPassword(password, user.password);
  if (!user || !correct) {
    return next(new AppError('Incorrect email or password!'), 400);
  }

  // 3) 如果正确生成并返回token
  createSendToken(user, 200, res);
});
// 创建并发送token
const createSendToken = (user, statusCode, res) => {
  // 1)注册令牌
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRES_IN,
  });
  
  // 2)向客户端发送要存储的cookie信息
  const cookieOptions = {
    expires: new Date(
      Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000,
    ),
    httpOnly: true,
  };

  if (process.env.NODE_ENV === 'production') cookieOptions.secure = true;

  res.cookie('jwt', token, cookieOptions);

  // 3)返回用户信息
  user.password = undefined;
  res.status(statusCode).json({
    data: 'success',
    token,
    data: { user },
  });
};
// 路由保护
exports.protect = catchAsync(async (req, res, next) => {
  // 1) Getting token and check of it's there
  let token;
  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith('Bearer')
  ) {
    token = req.headers.authorization.split(' ')[1];
  }

  if (!token) {
    return next(
      new AppError('You are not logged in! Please log in to get access', 401),
    );
  }
  // 2) Verification token
  const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
  // console.log(decoded);

  // 3) Check if user still exists(利用jwt的自包含特性)
  const freshUser = await User.findById(decoded.id);
  if (!freshUser) {
    return next(
      new AppError(
        'The user belonging to this token does no longer exist.',
        401,
      ),
    );
  }

  // 4) Check if user changed password after the token was issued
  if (freshUser.changedPasswordAfter(decoded.iat)) {
    return next(
      new AppError('User recently changed password! please log in again', 401),
    );
  }

  // GRANT ACCESS TO PROTECT ROUTE
  req.user = freshUser;
  next();
});

参考

JSON Web Tokens官网

转载自:https://juejin.cn/post/7383205958828933157
评论
请登录