百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

Nodejs 在实战中的校验用户信息(JWT、localStorage、Cookie)

qiyuwang 2024-10-31 15:51 14 浏览 0 评论

本文分别站在了客户端(reactjs)与服务端(nodejs)的角度,总结了整个用户校验过程各自的操作。

一 概念明晰

1.1 localStorage 和 Cookie

都是存储数据的方式

  • localStorage:储存在客户端(浏览器)本地
  • Cookie:存储在服务端,安全性更高。(是一个 HTTP 请求标头,由服务器通过 Set-Cookie 设置,存储到客户端的 HTTP cookie)

1.2 Token/JWT 和 SessionId

都是用户信息标识

  • Token:一个通用术语,是代表用户身份的字符串。它通常由服务器在用户成功登录后生成,并在用户进行后续请求时发送给服务器以验证其身份。
  • JWT(JSON Web Token):一种特殊的 Token。由三部分组成的字符串:Header(令牌类型和签名算法)、Payload(用户信息)、Signature组成
  • SessionId:用来识别和追踪用户会话的一串唯一的字符

本文主要讲JWT

二 JWT的生成与使用

jwt.io/

  1. 安装JWT库
npm i jsonwebtoken

2.登录时生成JWT

const jwt = require('jsonwebtoken');

const login = async (req, res) => {
   // ...登录成功后
   
   const token = jwt.sign(
      { userId: <userId>, username: <username> }, // 填入想存储的用户信息
      process.env.JWT_SECRET,                     // 秘钥,可以为随机一个字符串
      {
         expiresIn: "7d",                         // 其他选项,如过期时间
      }
   );
   
   // ...
};

接着就是选择存储方式:1.将token返回到客户端让客户端存储在localStorage;2.将token存储在服务端Cookie

3.调用其他请求时验证Token

// 验证的中间件

const authToken = async (req, res, next) => {
  
  // ... 根据存储方式拿到token
  const token = "your_token"
  
   try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET); // 传入token和秘钥
      // 拿到解出来的 { userId, username }
      // ... 进一步从数据库中判断这个用户信息是否存在
      // 将信息挂载req.user中供后续接口使用
      req.user = { userId, username, ... };
      next();
   } catch (error) {
       res.status(401).json({msg:"用户验证失败"})
   }
};

三 应用场景

  1. JWT & localStorage
  2. JWT & Cookie

3.1 存储在localStorage

  1. 服务端:将token返回给客户端
const login = async (req, res) => {
   // ...登录成功后
   // ...生成完token
   const token = "your_token"
   
   // 将token返回给客户端
   res.status(StatusCodes.OK).json({
       msg: '登录成功',
       token,
    });
};

2.客户端:将token存储到localStorage,并在后续请求中将token发送给服务端

为了方便管理,这里简单封装了下aixos:

import toast from 'react-hot-toast';

// 创建axios实例,把本地的token放在header中:
const axiosInstance = axios.create({
     baseURL: '/api/v1',
     timeout: 3000,
     headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }, // 每个请求都自动携带token
 });
 
 // 是否显示成功的提示或者失败的提示
 const defaultConfig = {
     showError: true,
     showSuccess: false
 }
 const request = (url: string, config= {}) => {
     const _config = {
         ...defaultConfig,
         ...config
     }
     const { data, params } = _config
     const method = _config.method || 'get'

     return axiosInstance.request({
         url,
         method,
         data: data || {},
         params: params || {},
     }).then((res) => {
         const data = res.data;
         _config.success && _config.success(data);
         if (_config.showSuccess) toast.success(data.msg || '请求成功');
         return data as TResData<T>
     }).catch((err) => {
         if (err.response.status >= 500) {
             toast.error('服务器发生错误,请稍后再试')
         }
         // 如果用户校验失败,重新返回登录页
         if (err.response.status === 401) {
             toast.error('用户凭证出现问题,请重新登录')
             location.href = '/login'  
         }
         // 其他错误
         let data = err.response.data
         _config.error && _config.error(data)
         if (_config.showError) toast.error(data.msg || '未知错误')
         return data
     })
 }

现在基于这个封装好的request,写一下示例:

(1) 登录时存储token

request('/login', {
      method: 'POST',
      data: {
         username,
         password,
      },
      showSuccess: true,
      success: (data) => {
         localStorage.setItem('token', data.token); // 登录成功后将token存储
         location.href = '/home'; // 跳转到主页 
      },
   });

(2)其他请求:自动在Header上携带token

request('/stats');

(3)退出登录:清除localstorage的token

request('/logout', {
  success: (data) => {
     localStorage.removeItem('token'); // 清除tokn
     location.href = '/login'; // 跳转到登录页
  },
});

3.服务端:拿到客户端发过来的token进行验证

// 用户验证中间件
const authToken = async (req, res, next) => {
    // 获取token
   const authHeader = req.headers.authorization;

   if (!authHeader || !authHeader.startsWith('Bearer ')) {
      res.status(401).json({msg:"No token provided"})
   }

   const token = authHeader.split(' ')[1];
   
   // 验证token
   try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      // 以mongose为例
      const user = await User.findById(decoded.userId).select('-password');
      req.user = { userId: user._id, username: user.username, email: user.email };
      next();
   } catch (error) {
       res.status(401).json({msg:"用户验证失败"})
   }
};

在其他请求中加上中间件:

app.use('/api/v1/jobs', authToken, jobsRoute);

3.2 存储在Cookie

  1. (可选)服务端:安装Cookie解析库
npm i cookie-parser
// app.js

const cookieParser = require('cookie-parser');
app.use(cookieParser());
// 或加密
// app.use(cookieParser(process.env.COOKIE_SECRET, { signedCookies: true }));

服务端:将token存储在Cookie中

const login = async (req, res) => {
   // ...登录成功后
   // ...生成完token
   const token = "your_token"
   
   // 安装cookie-parser后可以这样写
   const oneDay = 1000 * 60 * 60 * 24;
   res.cookie('token', token, {
      httpOnly: true,
      expires: new Date(Date.now() + oneDay),
      secure: process.env.NODE_ENV === 'production',
      signed: true,
   });
   
   // 它实际上进行操作是:
   /**
      let cookieString = `token=${token}; Expires=${oneDay}; HttpOnly`;
      if (process.env.NODE_ENV === 'production') {
         cookieString += '; Secure';
      }
      res.setHeader('Set-Cookie', cookieString);
   */
   
     res.status(StatusCodes.OK).json({
       msg: '登录成功'
    });
};


3.客户端:不需要存储token,也不需要在请求头携带token了,只需要根据服务端返回的status code来判断是否跳转回登录页

// 依旧是使用上面封装好的request

const axiosInstance = axios.create({
    baseURL: '/api/v1',
    timeout: 1000,
    // headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') },
});


// ...
.catch(()=>{
     if (err.response.status === 401) {
        toast.error('用户凭证出现问题,请重新登录')
        location.href = '/login'
    }
})

4.服务端:对于其他请求,拿到Cookie的token进行验证

// 用户验证中间件
const authToken = async (req, res, next) => {
   const token = req.cookie.token;  
   // 等同于:req.headers.cookie.split('=')[1]
   // 如果上面的signed为true, 则  const token = req.signedCookies.token;

   if (!token) {
      res.status(401).json({msg:"No token provided"})
   }

   try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET); // 传入token和秘钥
      // 拿到解出来的 { userId, username }
      // 将信息挂载req.user中供后续接口使用
      req.user = { userId, username, ... };
      next();
   } catch (error) {
       res.status(401).json({msg:"用户验证失败"})
   }
};

使用中间件

app.use('/api/v1/groups', authToken, groupsRoute);

5.服务端:对于退出登录,还需要清除Cookie的token

const logout = async (req, res) => {
   res.clearCookie('token')
   res.status(StatusCodes.OK).json({
      msg:'成功退出'
   })
}




文章转自:https://juejin.cn/post/7372842988685688871



相关推荐

windows开启telnet服务,检测远程服务端口是否可以连通

本文介绍windwos开启telnet服务,telnet服务一般可以用于检测远程主机的某个端口服务是否可以连通,在日常的工作中,我们经常会遇到在本地的windows检测远程服务端口是否可以连通。win...

仅在Web登录新华三交换机条件下启用设备Telnet登录方式

概述Web登录新华三交换机可以在“网络-服务”页面中启用设备Telnet服务或SSH服务,也可以在“设备-管理员”设置管理员用户的可用服务,然而,在设备Web页面中,无法设置lineVTY用户线【l...

思科交换机,路由器如何关闭telnet 开启ssh服务

SSH为建立在应用层基础上的安全协议。SSH是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用SSH协议可以有效防止远程管理过程中的信息泄露问题。今天我们就来说说思科交换机,路...

智能化弱电行业常用的DOS命令,掌握了你也能成为...

前言在做智能化弱电项目时,前端摄像头设备安装结束后,我们会对网络摄像头进行调试,调试过程中会遇到前端摄像头没有图像或者图像出来了画面卡顿的现象。我们会采用ping命令来测试网络的连通性和网络承载能力。...

「干货」eNSP模拟器之配置Telnet登录

配置说明:配置Telnet,使R2(模拟PC)通过SW1登录到R1进行管理和配置。操作步骤:system-view##进入系统视图[Huawei]sysnameR1##改名为R1[R1]int...

win11开启telnet服务怎么操作 win11打开telent指令是什么

telnet服务是我们在进行远程连接的时候,必须要打开的一项功能。但是有不少用户们不清楚在windows11系统中怎么开启telnet服务。今天小编就使用详细的图文教程,来给大家说明一下打开telen...

华三(H3C)交换机Telnet的远程登陆

一,配置交换机管理IP[SW1]vlan20//创建管理vlan[SW1]interfacevlan20//进入vlan接口[SW1-Vlanif20]ipaddress192.168....

win10 telnet命令怎么查看端口是否打开

可能大家也会遇到这个问题,win10telnet命令查看端口是否打开的步骤是什么?具体方法如下:1、键盘输入快捷键WIN+R,打开运行窗口。2、输入cmd,点击确定按钮。3、弹出cmd命令行窗...

Windows 7如何打开Telnet功能(win7系统打开telnet)

Windows7默认安装后是没有开启telnet客户端功能的,例如,我们在开始菜单中输入cmd,然后使用telnet命令,会弹出下图提示:‘telnet’不是内部或外部命令,也不是可运行程序或批处理文...

为锐捷路由器交换机开启web和telnet,实现轻松管理

笔者上一篇文章写了关于锐捷二层交换机配置教程,那么接下来讲一下锐捷的路由交换设备配置web、telnet技巧。同样,今天的教程也是基于命令行,比较简单,适合新手小白进行学习。准备工作配置前准备:con...

一文学会telnet命令的用途和使用方法

Telnet是一个古老的远程登录协议,可以让本地计算机获得远程计算机的工作能力。它采用了TCP的可靠连接方式,可以连接任何网络互通的远程计算机。不过由于它采用了明文传输方式,存在安全风险,目前已经很少...

Telnet命令是什么?如何使用?(telnet命令在哪里开启)

telnet命令是一个常用的远程登陆工具,使用它,我们可以快捷地登陆远程服务器进行操作。那么如何使用telnet命令呢?首先,我们需要打开telnet功能,任何电脑默认是关闭此功能的,开启方式如下:打...

win11系统如何开启telnet服务(拷贝版本)

  我们要知道,Telnet协议是Internet远程登陆服务的标准协议,可以使用户在本地计算机上完成远程主机的工作,不过对于一些刚接触win11中文版系统的用户来说,可能还不知道telnet服务在哪...

如何开启telnet客户端(如何开启telnet服务)

Telnet协议是TCP/IP协议家族中的一员,是Internet远程登陆服务的标准协议和主要方式,Telnet是常用的远程控制Web服务器的方法。工作中经常用到telnet客户端,但在windows...

Telnet 是什么,如何启用它?(telnet有什么用)

对于Internet等TCP/IP网络,Telnet是一个终端仿真程序。Telnet软件在您的系统上运行并将您的个人计算机链接到网络服务器。它将所有数据转换为纯文本这一事实被认为是易受...

取消回复欢迎 发表评论: