首页 > 编程语言 >Nodejs 在实战中的校验用户信息(JWT、localStorage、Cookie)

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

时间:2024-05-27 11:34:14浏览次数:28  
标签:const Nodejs res req JWT token localStorage Cookie data

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

一 概念明晰

都是存储数据的方式

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

1.2 Token/JWT 和 SessionId

都是用户信息标识

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

本文主要讲JWT

二 JWT的生成与使用

https://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 }));
    
  2. 服务端:将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进行验证

    其他请求的请求头部的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:'成功退出'
       })
    }
    

标签:const,Nodejs,res,req,JWT,token,localStorage,Cookie,data
From: https://www.cnblogs.com/sanhuamao/p/18215163

相关文章

  • 使用树梅派搭建Golang、Python、NodeJs的开发服务器
    使用树梅派搭建Golang、Python、NodeJs的开发服务器安装系统安装rpi-imagersudoaptinstallrpi-imager打开rpi-imager烧写RaspberryPiOSLite(64-bit)系统设置好用户名、密码、wifi、ssh等信息上电修改镜像源备份/etc/apt/sources.listsudocp/etc/apt......
  • jwt总结文档
    Token:令牌,本质是一个字符串,JWT是token的一种具体实现方式,其全称是JSONWebTokentoken进行用户身份验证的流程:1.客户端使用用户名和密码请求登录2.服务端收到请求,验证用户名和密码3.验证成功后,服务端会签发一个token,再把这个token返回给客户端4.客户端收到token后可以把......
  • Nodejs安装及配置,包含Windows和Linux两种平台
    目录1.下载安装包2.Windows下安装3.Linux下安装4.使用Vite创建Vue项目5.结语Node.js是前端开发的必备工具,特别是在使用Vue或React开发项目时,需要npm安装依赖、运行开发环境以及项目打包,这里就分别介绍一下在Windows和Linux平台如何安装Nodejs。文章结尾,笔者还会......
  • Nodejs的ORM--Sequelize-一万六千字-详细教程
    本文来介绍Sequelize,一个基于Node.js的ORM(对象关系映射)工具,并详细介绍其用法。Sequelize用于在应用中使用JavaScript来操作关系型数据库,例如MySQL、PostgreSQL等。本文内容较多,可作为Sequelize的参考手册来阅读。开始使用核心概念Sequelize是一个基于JavaSc......
  • Node.js —— 前后端的身份认证 之用 express 实现 JWT 身份认证
    JWT的认识什么是JWT        JWT(英文全称:JSONWebToken)是目前最流行的跨域认证解决方案。JWT的工作原理        总结:用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。  JWT的组成部分......
  • nvm介绍、下载、安装、配置及使用,(Node Version Manager)nodejs版本管理切换工具
    1、介绍nvm在Web前端项目开发过程中,由于各种前端框架、插件以及Nodejs、Npm的飞速更新,在项目新开发或对老项目进行更新维护时,有些项目版本的配置和当前Node、Npm环境不匹配,导致运行报错,甚至都无法启动。nvm的出现就是为了解决以上问题的,nvm是一个Node.js版本管理器,......
  • nodejs安装及环境配置
    Node.js的安装及环境配置可以遵循以下步骤:一、Node.js的安装访问Node.js的官方网站(https://nodejs.org/en/),下载对应你操作系统的Node.js安装包。找到下载的安装包目录,双击进行安装。在安装过程中,接受用户协议,选择安装的位置(最好是英文路径,不要有空格)。选择安装项,一般选择......
  • nodeJS文件操作
    const{log}=require("console");constfs=require("fs");constpath=require("path");constfilename=path.resolve(__dirname,"./myfiles1.txt");//console.log(filename)//fs.readFile(filename,(err,content)......
  • nodeJS 内置对象
    //const{log}=require("console");//const{argv}=require("process");//log(__dirname)//setImmediate(()=>{//log(__filename)//})//constbuffer=Buffer.from('a1','utf-8')//log(buffer)/......
  • NodeJS-高性能编程-全-
    NodeJS高性能编程(全)原文:zh.annas-archive.org/md5/DF276329F6BD35B176ABE023A386AF47译者:飞龙协议:CCBY-NC-SA4.0前言在像Node.js这样的平台上实现高性能意味着要了解如何充分利用硬件的各个方面,并帮助内存管理发挥最佳作用,并正确决定如何设计复杂的应用程序。如果您的......