Node.js:session JWT

news2024/12/23 14:15:34

Node.js:session & JWT

    • session
      • cookie
      • session
      • express-session
    • JWT
      • express-jwt & jsonwebtoken


session

HTTP协议是无状态的,客户端的每次HTTP请求都是独立的,多个请求之间没有直接的关系,服务器不会保留每次HTTP请求的状态。

简单来说,当服务器接收到多个请求时,无法确定这些请求是否来自于同一用户,无法完成身份标识。为此,浏览器提供了cookie的方法,来标识一个客户的信息。


cookie

cookie是存储在用户浏览器中的一段不超过4 kb的字符串,其由键值对,以及其他几个控制cookie有效期、安全性、使用范围的可选属性构成。

访问baidu.com,进入控制面板,查看ApplicationCookies,可以看到baidu.com对应的信息:

在这里插入图片描述

右侧这些内容,都是baidu.com使用的cookie。其中namevalue是键值对存储信息,其余的是一些控制信息。

浏览器会记录用户在每个域名下访问时的cookie信息,并保存下来。当客户端发起请求时,浏览器会自动把该域名下的所有未过期的cookie一同发送到服务器。

也就是说,每个域名都有自己独立的cookie,并且浏览器会自动完成发送。

在这里插入图片描述

浏览器发送请求时,如果服务器响应了cookie信息,那么后续浏览器发送其他请求,只要属于同一域名,都会自动把之前收到的cookie一起发送给服务器。这样服务器就可以通过请求得知,哪些请求属于同一个用户。

但是cookie是一种不安全的身份验证机制,一方面用户可以伪造一个cookie发送给服务器,另一方面cookie在报文中是完全可见的,如果存储账号密码这样的信息,就很容易泄露。


session

为了解决用户伪造信息的问题,于是有了session认证机制,sessioncookie多一个检验步骤,当用户发送一个带有cookie的请求时,服务端不是简单的接收请求,而是会判断这个cookie是否合法。

在这里插入图片描述

如图,当用户发起登录请求时,服务端生成一个cookie字符串,发送回给客户端。后续客户端每次通信,都要把cookie携带在请求内,当客户端收到请求后,验证客户端的cookie是否合法,只有信息合法才会发送响应

在这个过程中,客户端全程都只持有一个加密后的字符串,而服务端持有用户的相关信息。只要服务端拿到字符串后,就会去查找这个字符串对应的信息,然后做出后续操作。因为客户端并不接触到具体信息,所以这个认证方式比cookie安全很多。

express-session

Node.js中,express也提供了非常方便的中间件express-session,可以直接操作session

下载:

npm i express-session

配置中间件:

const express = require('express')
const app = express()

// 请配置 Session 中间件
const session = require('express-session')
app.use(
  session({
    secret: 'hello world',
    resave: false,
    saveUninitialized: true,
  })
)

通过require导入这个模块后,就可以直接使用了。express-session是一个中间件,需要绑定到app上,绑定时需要传入三个参数,其中后两个参数是固定写法,第一个参数secret是加密字符串,可以随意填写一个字符串。session会把信息基于该字符生成session id

当成功配置中间件后,req会多出一个属性session

app.get('/login', (req, res) => {
  if (req.query.name !== 'root' || req.query.password !== '123456') {
    return res.send({ status: 1, msg: '登录失败' })
  }

  req.session.name = req.query.name // 用户的信息
  req.session.password = req.query.password // 用户的密码
  req.session.islogin = true // 用户的登录状态

  res.send({ status: 0, msg: '登录成功' })
})

当服务器接收到来自客户端的请求,先进行简单的账号密码验证,此处固定写为root 123456,具体业务中可能还涉及到数据库的查询操作。

如果登录成功,就把相关信息写入到req.session中,随后session会基于之前的secret字符串生成一个session id,最后send发送数据时,把session id作为cookie发送给客户端。

在浏览器访问/login

在这里插入图片描述

可以看到,127.0.0.1下多出了一个cookie,其内容为connect.sid,这就是session id。这个connect.id内部,是一个看似乱码的字符串,其不包含session真正存储的信息,真正的信息存储在服务器上。

写一个/test路由,用于测试session

app.get('/test', (req, res) => {
  if (!req.session.islogin) {
    return res.send({ status: 1, msg: 'fail' })
  }

  res.send("你好 " + req.session.name)
})

在这个路由内部,会检测该用户是否带有session.islogin,也就是该用户是否登录了。想要这个if成立,那么客户端发起请求时就必须带上session id,服务器会查找session id匹配的信息,并且存放在res.session对象中。

浏览器访问:

在这里插入图片描述

这次访问,没有在url尾部添加相关信息,但是服务器依然知道当前是root用户在操作,就是因为检测到了session id

当用户退出时,服务器可以主动销毁session

app.get('/logout', (req, res) => {
  req.session.destroy()
  res.send("退出成功")
})

当执行req.session.destroy时,服务器会销毁这个域对应的session信息,注意不是销毁所有的session,只销毁发起请求的req对应的信息。

当用户访问/logout后,可能浏览器内还是存有session id,但是这个session id已经失效了,如果还需要继续访问,就要重写登录,得到一个新的session id


JWT

session的认证机制,是基于cookie实现的,但是cookie不支持跨域访问,所以如果要跨域访问,就需要做很多额外的跨域操作。

JWT可以解决跨域认证问题,全称JSON Web Token,在需要面对跨域时,推荐使用JWT完成身份认证。

JWT的原理和session很类似:

在这里插入图片描述

当用户提交指定的信息,服务器就会生成一个token字符串,并把这个字符串发送回给客户端,客户端把token存储到LocalStorageSessionStorage。后续客户端发起请求,只要携带这个token,服务端就可以通过token获取到用户信息。

JWTsession的有以下区别:

  1. 加密后的字符串的存储位置不同
  2. session的信息存储在服务器本地,JWT的信息存储在加密的字符串中
  3. session由浏览器自主携带,但是JWT需要前端发送ajax请求时手动添加

因为session使用cookie存储加密后的字符串,cookie不支持跨域。所以JWT就不再使用cookie存储加密后的字符串了,而是使用浏览器的本地临时存储。

token字符串由三部分组成,分别是Header头部、Payload有效载荷、Signature签名,三部分之间由.分隔。

其中HeaderSignature是安全性相关的部分,而Payload才是真正存储的数据


express-jwt & jsonwebtoken

Node.js中使用JWT,需要使用两个相关的包:

  • jsonwebtoken:用于生成token字符串
  • express-jwt:用于将token字符串还原为json对象

安装包:

npm i jsonwebtoken express-jwt
  1. 定义密钥
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

const secretKey = '@#$%fhalkfjwpw@#$%'

在对数据加密时,要使用一个字符串的密钥,这个密钥可以提前定义到一个变量中,并且越复杂越好。

  1. 生成token

生成token字符串,可以通过jwt.sign方法进行加密

语法:

jwt.sign({ key: value }, secretKey, { expiresIn: 'times' })

其第一个参数传入要加密的对象,第二个参数传入使用的密钥,第三个参数是一个配置对象,对象中的expiresIn属性定义了数据的过期时间,超过该时间后,数据无效。

最后该方法会返回加密完成后的字符串。

示例:

app.post('/login', function (req, res) {
  if (req.body.name !== 'root' || req.body.password !== '123456') {
    return res.send({
      status: 400,
      message: '登录失败!',
    })
  }

  const tokenStr = jwt.sign({ username: req.body.name }, secretKey, { expiresIn: '30s' })
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr, // 要发送给客户端的 token 字符串
  })
})

以上示例中,将信息req.query.username加密为了字符串tokenStr,有效期60s。最后通过res.send发送回给客户端,token属性内部传入加密后的字符串。

使用postman发送请求:

在这里插入图片描述

这样就可以得到服务端生成的token

  1. 读取token

读取token使用express-jwt中间件,先把中间件注册到app中:

app.use(expressJWT({ secret: secretKey }))

注册中间件时,第一个参数传入一个对象,对象内部的secret属性指定之前指定的密钥。

expressJWT中间件会检测请求的token是否合法,并且解析。以上代码,将所有路由都注册了这个中间件,但是用户在/login之前是没有得到token,这个中间件不能对/login注册:

app.use(expressJWT({ secret: secretKey }).unless({ path: "/login" }))

当注册中间件成功后,req下就会多出一个user属性,这个属性内部是token字符串解析出来的内容。

app.get('/test', function (req, res) {
  res.send("你好" + req.user.username)
})

其中req.user就是expressJWT解析出来的对象,这个对象内部有之前写入的username属性。

使用postman发送post /test请求:

在这里插入图片描述

在请求头中,要携带一个Authorization选项,选项的内容是Bearer token字符串。注意在Bearertoken之间,有一个空格。这个添加请求头的过程,浏览器并不完成,而是由客户端的ajax完成。

最后得到你好 root响应,说明前后是一个用户。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2231593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

后台管理系统的通用权限解决方案(七)SpringBoot整合SpringEvent实现操作日志记录(基于注解和切面实现)

1 Spring Event框架 除了记录程序运行日志,在实际项目中一般还会记录操作日志,包括操作类型、操作时间、操作员、管理员IP、操作原因等等(一般叫审计)。 操作日志一般保存在数据库,方便管理员查询。通常的做法在每个…

曹操出行借助 ApsaraMQ for Kafka Serverless 提升效率,成本节省超 20%

本文整理于 2024 年云栖大会主题演讲《云消息队列 ApsaraMQ Serverless 演进》,杭州优行科技有限公司消息中间件负责人王智洋分享 ApsaraMQ for Kafka Serverless 助力曹操出行实现成本优化和效率提升的实践经验。 曹操出行:科技驱动共享出行未来 曹操…

Mysql常用语法一篇文章速成

文章目录 前言前置环境数据库的增删改查查询数据查询所有条件查询多条件查询模糊查询分页查询排序查询分组查询⭐️⭐️关联查询关联分页查询 添加数据insert插入多条记录不指定列名(适用于所有列都有值的情况) 更新数据更新多条记录更新多个列更新不满足条件的记录 删除统计数…

信息安全数学基础(43)理想和商环

理想(Ideal) 定义: 设R是一个环,I是R的一个非空子集。如果I满足以下条件,则称I为R的一个理想: 对于任意的r1, r2 ∈ I,有r1 - r2 ∈ I(加法封闭性)。对于任意的r ∈ I&am…

node.js下载、安装、设置国内镜像源(永久)(Windows11)

目录 node-v20.18.0-x64工具下载安装设置国内镜像源(永久) node-v20.18.0-x64 工具 系统:Windows 11 下载 官网https://nodejs.org/zh-cn/download/package-manager 版本我是跟着老师选的node-v20.18.0-x64如图选择 Windows、x64、v20.18…

《JVM第3课》运行时数据区

无痛快速学习入门JVM,欢迎订阅本免费专栏 运行时数据区结构图如下: 可分为 5 个区域,分别是方法区、堆区、虚拟机栈、本地方法栈、程序计数器。这里大概介绍一下各个模块的作用,会在后面的文章展开讲。 类加载子系统会把类信息…

苏州金龙新V系客车创新引领旅游出行未来

10月25日,为期三天的“2024第六届旅游出行大会”在风景秀丽的云南省丽江市落下帷幕。本次大会由中国旅游车船协会主办,全面展示了中国旅游出行行业最新发展动态和发展成就,为旅游行业带来全新发展动力。 在大会期间,备受瞩目的展车…

QML旋转选择器组件Tumbler

1. 介绍 Tumbler是一个用于创建旋转选择器的组件。它提供了一种直观的方式来让用户从一组选项中进行选择,类似于转盘式数字密码锁。网上找的类似网图如下: 在QML里,这种组件一共有两个版本,分别在QtQuick.Extras 1.4(旧)和QtQuic…

思科路由器静态路由配置

转载请注明出处 该实验为静态路由配置实验,仅供参考 选择三台2811路由器 关闭电源-安装模块-开启电源(以R1为例,其他两台也是一样操作!) 连线。注意R1与R3之间、R3与R2之间用DCE串口线(如下图)…

闪存学习_2:Flash-Aware Computing from Jihong Kim

闪存学习_2:Flash-Aware Computing from Jihong Kim【1】 一、三个闪存可靠性问题二、内存的分类三、NAND 闪存和 NOR 闪存四、HDD和SSD比较Reference 一、三个闪存可靠性问题 耐性(即寿命):最多能经受编程和擦除的次数。数据保留…

双指针问题解法集(一)

1.指针对撞问题(利用有序数组的单调性衍生) 1.1LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)​​​​​​ 1.1.1题目解析 有序数组,找到和为price的两个元素,只需要一个解即可。 1.1.2算法…

深度学习(十):伦理与社会影响的深度剖析(10/10)

深度学习:伦理与社会影响的深度剖析 一、深度学习的伦理挑战 (一)数据隐私之忧 深度学习模型的训练往往需要大量数据,而数据的收集过程可能会侵犯个人隐私。例如,据统计,面部识别技术在全球范围内每天会收…

网络模型——二层转发原理

网课地址:网络模型_二层转发原理(三)_哔哩哔哩_bilibili 一、路由交换 网络:用来信息通信,信息共享的平台。 网络节点(交换机,路由器,防火墙,AP)介质&#…

探索NetCat:网络流量监测与数据传输的利器

从简单的数据传输到复杂的网络调试,NetCat的灵活性和多功能性让人赞叹不已,在这篇文章中我将深入探讨NetCat的魅力,揭示它的基本功能、实用技巧以及在日常工作中的应用场景,发现如何用这一小工具提升的网络技能与效率。 目录 Net…

提高交换网络可靠性之链路聚合

转载请注明出处 该实验为链路聚合的配置实验。 1.改名,分别将交换机1和交换机2改名为S1,S2,然后查看S1,S2的STP信息。以交换机1为例👇。 2.交换机S1,S2上创建聚合端口,将端口加入聚合端口。以S…

Kubernetes:(三)Kubeadm搭建K8s 1.20集群

文章目录 一、Kubeadm安装流程二、实验1.环境准备2.所有节点安装kubeadm,kubelet和kubectl(除了Harbor节点)3.部署 Dashboard4.安装Harbor私有仓库 一、Kubeadm安装流程 集群名称IP地址安装软件master(2C/4G,cpu核心数…

使用MongoDB Atlas构建无服务器数据库

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用MongoDB Atlas构建无服务器数据库 MongoDB Atlas 简介 注册账户 创建集群 配置网络 设置数据库用户 连接数据库 设计文档模式…

Windows 10 安装使用Docker踩过的坑和解决-31/10/2024

目录 环境版本 一、Docker Desktop双击启动没反应,open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified. 二、Docker Desktop运行run命令时显示错误HTTP code 500 并且错误大意是服务器拒绝访问 三、Docker Engine stopped/启动…

parted 磁盘分区

目录 磁盘格式磁盘分区文件系统挂载使用扩展 - parted、fdisk、gdisk 区别 磁盘格式 parted /dev/vdcmklabel gpt # 设置磁盘格式为GPT p # 打印磁盘信息此时磁盘格式设置完成! 磁盘分区 开始分区: mkpart data_mysql # 分区名&…

论 ONLYOFFICE:开源办公套件的深度探索

公主请阅 引言第一部分:ONLYOFFICE 的历史背景1.1 开源软件的崛起1.2 ONLYOFFICE 的发展历程 第二部分:ONLYOFFICE 的核心功能2.1 文档处理2.2 电子表格2.3 演示文稿 第三部分:技术架构与兼容性3.1 技术架构3.2 兼容性 第四部分:部…