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

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

qiyuwang 2024-10-31 15:51 16 浏览 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



相关推荐

基于Docker方式安装与部署Camunda流程引擎

1Camunda简介官网:https://docs.camunda.org/manual/7.19/installation/docker/Camunda是一个轻量级、开源且高度灵活的工作流和决策自...

宝塔Linux面板如何部署Java项目?(宝塔面板 linux)

通过宝塔面板部署Java还是很方便的,至少不需要自己输入tomcat之类的安装命令了。在部署java项目前,我还是先说下目前的系统环境,如果和我的系统环境不一样,导致部署不成功,那你可能需要去找其他资...

浪潮服务器如何用IPMI安装Linux系统

【注意事项】此处以浪潮服务器为例进行演示所需使用的软件:Chrome浏览器个人PC中需要预先安装java,推荐使用jdk-8u181-windows-x64.exe【操作步骤】1、在服务器的BIOS中...

Centos7环境Hadoop3集群搭建(hadoop集群环境搭建实验报告)

由于项目需要存储历史业务数据,经过评估数据量会达到100亿以上,在原有mongodb集群和ES集群基础上,需要搭建Hbase集群进行调研,所以首先总结一下Hadoop集群的搭建过程。一、三个节点的集群...

Hadoop高可用集群搭建及API调用(hadoop高可用原理)

NameNodeHA背景在Hadoop1中NameNode存在一个单点故障问题,如果NameNode所在的机器发生故障,整个集群就将不可用(Hadoop1中虽然有个SecorndaryNameNo...

使用Wordpress搭建一个属于自己的网站

现在开源的博客很多,但是考虑到wordpress对网站的seo做的很好,插件也多。并且全世界流量排名前1000万的网站有33.4%是用Wordpress搭建的!所以尝试用Wordpress搭建一个网站...

Centos 安装 Jenkins(centos 安装ssh)

1、Java安装查看系统是否已安装Javayumlistinstalled|grepjava...

Java教程:gitlab-使用入门(java中的git)

1导读本教程主要讲解了GitLab在项目的环境搭建和基本的使用,可以帮助大家在企业中能够自主搭建GitLab服务,并且可以GitLab中的组、权限、项目自主操作...

Dockerfile部署Java项目(docker部署java应用)

1、概述本文主要会简单介绍什么是Docker,什么是Dockerfile,如何安装Docker,Dockerfile如何编写,如何通过Dockerfile安装jar包并外置yaml文件以及如何通过do...

如何在Eclipse中搭建Zabbix源码的调试和开发环境

Zabbix是一款非常优秀的企业级软件,被设计用于对数万台服务器、虚拟机和网络设备的数百万个监控项进行实时监控。Zabbix是开放源码和免费的,这就意味着当出现bug时,我们可以很方便地通过调试源码来...

Java路径-02-Java环境配置(java环境搭建及配置教程)

1Window环境配置1.1下载...

35.Centos中安装python和web.py框架

文章目录前言1.Centos7python:2.Centos8python:3.进行下载web.py框架然后应用:4.安装好之后进行验证:5.总结:前言...

《我的世界》服务器搭建(我的世界服务器如何搭建)

1.CentOS7环境1.1更改YUM源#下载YUM源文件curl-o/etc/yum.repos.d/CentOS-Base.repohttps://mirrors.aliyun.com...

CentOS 7 升级 GCC 版本(centos7.4升级7.5)

1.GCC工具介绍GCC编译器:...

Linux安装Nginx详细教程(linux安装配置nginx)

环境准备1.因为Nginx依赖于gcc的编译环境,所以,需要安装编译环境来使Nginx能够编译起来。命令:yuminstallgcc-c++显示完毕,表示安装完成:2.Nginx的http模块需要...

取消回复欢迎 发表评论: