当前位置: 首页 > news >正文

Node.js-1

Node.js 简介

定义Node.js 是一个跨平台 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序

为什么 Node.js 能执行 JS 代码

  • Chrome 浏览器能执行 JS 代码,依靠的是内核中的 V8引擎(即:C++程序)

在这里插入图片描述

  • Node.js 是基于 Chrome V8 引擎进行封装的
  • 注意:Node.js 环境没有 DOM 和 BOM 等内容,但是它也有自己独立的 API

模块化

基本认知

Node.js 中根据模块来源不同,将模块分为了3大类,分别是:

  • 内置模块(内置模块是由 Node.js 官方提供的,例如fs、path、http等)
  • 自定义模块(用户创建的每一个 .js 文件,都是自定义模块)
  • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要提前下载)

加载模块

使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用(注意:使用 require() 方法加载其他模块时,会执行被加载模块中的代码)

// 加载内置模块
const fs = require('fs')// 加载用户自定义的模块
const myModule = require('./myModule.js')// 加载第三方模块
const moment = require('moment')

module 对象

  • module 对象

每个自定义的模块中都有一个 module 对象,它里面存储了和当前模块有关的信息(打印结果,如下所示)

Module {id: '.',path: 'E:\\Desktop\\jsProject',exports: {},filename: 'E:\\Desktop\\jsProject\\test.js',loaded: false,children: [],paths: ['E:\\Desktop\\jsProject\\node_modules','E:\\Desktop\\node_modules','E:\\node_modules']
}
  • module.exports 对象
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象
【myModule.js文件】module.exports.username = 'Jack'
module.exports.sayHello = () => { console.log('Hello') }
【text.js文件】const m = require('./myModule')
console.log(m)	// { username: 'Jack', sayHello: [Function (anonymous)] }

模块化规范

Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖

CommonJS 规定:

  • 每个模块内部,module 变量代表当前模块
  • module 变量是一个对象,它的 exports 属性(即:module.exports)是对外的接口
  • 加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块

npm与包

基本介绍

  • Node.js 中的第三方模块又叫做包
  • npm 公司提供了一个包管理工具,名字叫做 Node Package Manager(简称 npm 包管理工具),这个工具随着 Node.js 的安装被一起安装到了用户的电脑上
  • npm 包管理工具能够从 https://registry.npmjs.org/ 服务器把用户需要的包(第三方模块)下载到本地使用
  • 可以在终端执行 npm -v 命令,来查看自己电脑上所安装的 npm 包管理工具的版本号
  • 如果有需要的话,可以从"全球最大的包共享平台"搜索自己想要的包:https://www.npmjs.com/

npm 体验

目标:通过npm包管理工具,下载 moment 包,并输出格式化时间

  • 在终端输入npm命令,下载 moment 包
npm install moment
  • 简写
npm i moment
  • 指定版本号(不指定版本号,默认安装最新版本的包)
npm i moment@2.22.2
  • 输出格式化时间(详细操作,自行查阅官方文档)
// 1. 导入 moment 包
const moment = require('moment')// 2. 使用包里面的方法
const dt = moment.format('YYYY-MM-DD HH:mm:ss')
console.log(dt)	// 输出格式化时间

注意事项

  • 初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件

  • node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包

  • package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址

  • 注意:不要手动修改 node_modules 或 package-lock.json 文件中的任何代码,npm 包管理工具会自动维护它们

  • npm i moment@2.22.2 中的版本号由三位数字组成

每一位数字所代表的含义:第一位数字:大版本(例如:底层重构)第二位数字:功能版本(例如:新功能的推出)第三位数字:Bug修复版本(例如:修复某个Bug)版本号提升的规则:只要前面的版本号增长了,后面的版本号"归零"

包管理配置文件

基本认知

npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置信息,例如:- 项目的名称、版本号、描述等- 项目中都用到了哪些包- 哪些包只在开发期间会用到- 哪些包在开发和部署时都需要用到
问题:在多人协作时,第三方包的体积过大,不方便团队成员之间共享项目源代码解决方案:共享时剔除 node_modules 目录,让其他成员根据 package.json 配置文件,自行下载第三方模块(注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中)

快速创建

  • npm 包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建 package.json 配置文件
  • 该命令只能在英文的目录下成功运行
  • 当我们后续运行 npm install 命令去安装其他的包时,npm 包管理工具会自动更新配置文件的信息,我们不需要手动修改它
npm init -y

一次性安装所有包

  • 执行 npm install 命令时,npm 包管理工具会先自动读取 package.json 配置文件中的 dependencies 节点
  • 读取到记录的所有依赖包名称和版本号之后,npm 包管理工具会把这些包一次性下载到项目中
npm install

卸载包

  • 命令执行成功后,会把卸载的包,自动从 package.json 的 dependencies 中移除掉
npm uninstall 包的名称

devDependencies 节点

  • 如果某些包只在项目开发阶段用到,在项目上线之后不会使用,建议把这些包记录到 devDependencies 节点中
npm i 包的名称 -D或者npm i 包的名称 --save-dev

npm 镜像源

# 查看当前的下包镜像源
npm config get registry# 将下包的镜像源切换成淘宝镜像源
npm config set registry https://registry.npmmirror.com# 检查镜像源是否下载成功
npm config get registry

Node.js 内置模块

fs 文件系统模块

// 1. 导入 fs 模块对象
const fs = require('fs')// 2. 写入文件
//      参数1:要写入内容的文件的存放路径
//      参数2:表示要写入的内容
//      参数3:写入的格式(默认utf8)
//      参数3:回调函数
const info = 'Hello World'
fs.writeFile('./test.txt', info, 'utf8', function (err) {if (err) console.log(`错误信息${err}`)else console.log(`写入数据成功(数据信息:${info}`)// 3. 读取文件//      参数1:读取文件的存放路径//      参数2:读取文件时候采用的编码格式(默认utf8)//      参数3:回调函数,拿到读取失败和成功的结果fs.readFile('./test.txt', 'utf8', (err, data) => {if (err) console.log(`错误信息${err}`)else console.log(`读取数据成功(数据信息:${data.toString()}`) // data 是 buffer 16 进制数据流对象,需要转换为字符串格式})
})

path 路径模块

  • 获取当前文件所处的目录
// __dirname 表示获取当前文件所处的目录(文件夹)
console.log(__dirname)
  • 路径拼接
// 导入 path 模块
const path = require('path')// 注意: ../会抵消前面的路径
const pathStr = path.join('/a', '/b/c', '../', '/d', '/e')
console.log(pathStr)    // 输出路径为:\a\b\d\e
  • 获取文件名、扩展名
// 导入 path 模块
const path = require('path')// 定义文件的"存放路径"
const filePath = '/a/b/c/index.html'// 从"存放路径"中获取文件名(包含扩展名部分)
const fileName1 = path.basename(filePath)
console.log(fileName1)   // 输出结果:index.html// 从"存放路径"中获取文件名(不包含扩展名部分)
const fileName2 = path.basename(filePath, '.html')
console.log(fileName2)   // 输出结果:index// 从"存放路径"中获取文件扩展名
const extName = path.extname(filePath)
console.log(extName)    // 输出结果:.html

http 模块

// 1. 导入 http 模块
const http = require('http')
const { resolve } = require('path')// 2. 创建 web 服务器实例
const server = http.createServer()// 3. 为服务器实例绑定 request 事件
// req 是请求对象,包含了与客户端相关的数据和属性
// res 是响应对象
// res.end() 方法用于向客户端发送指定的内容,并结束本次请求的处理过程
server.on('request', (req, res) => {// req.url 是客户端请求的 URL 地址const url = req.url// req.method 是客户端请求的 method 类型const method = req.method// 为了防止中文乱码的问题,需要设置响应头res.setHeader('Content-Type', 'text/html; charset=utf-8')// res.end() 方法res.end(`<h1>您的请求方式为:${method} 请求</h1>`)
})// 4. 启动服务器
server.listen(80, () => {console.log('server running at http://127.0.0.1:80')
})

第三方 API 模块

express

基本介绍

  • Express 是基于 Node.js 平台的 Web 开发框架,专门用来创建 Web 服务器
  • Express 本质上就是一个第三方模块,相较与 Node.js 内置的 http 模块,它使用起来更简单,开发效率更高(Express 是基于 http 模块进一步封装出来的)
  • Express 中文官网:https://www.expressjs.com.cn/

模块使用

  • 安装
npm i express@4.17.1
  • 创建 Web 服务器
// 1. 导入 express
const express = require('express')// 2. 创建 web 服务器
const app = express()// 4. 监听客户端的 GET 和 POST 请求
// GET      app.get('请求URL', (请求对象, 响应对象) => { 处理函数 })
// POST     app.post('请求URL', (请求对象, 响应对象) => { 处理函数 })
app.get('/user', (req, res) => {res.send({ name: 'Jack', age: 20, gender: '男' })    // 响应一个 JSON 对象
})app.post('/user', (req, res) => {res.send('订单提交成功,请等待商家发货')    // 响应
})// 5. 获取URL携带的查询参数
app.get('/', (req, res) => {// 默认情况下,req.query 是一个空对象// 查询参数 ?name=Jack&age=20// 可通过 req.query.name 与 req.query.age 获得其值console.log(req.query)res.send(req.query)
})// 6. 获取URL中的动态参数
app.get('/user/:id/:name', (req, res) => {// req.params 默认是一个空对象,里面存放着,通过冒号识别出来的参数值res.send(req.params)
})// 3. 调用 app.listen(端口号, 启动成功后的回调函数)
app.listen(80, () => {console.log('Server started successfully at http://127.0.0.1/')
})

托管静态资源

  • 认识 express.static()
介绍:express 提供了一个非常好用的函数,叫做 express.static()通过它,我们可以非常方便地创建一个静态资源服务器例如:项目目录下有一个 public 文件夹,里面存放了图片、html、css、js文件,通过下面的代码就可以将它们对外开放访问了注意:express 在指定的静态文件中查找文件,并对外提供资源的访问路径,因此,存放静态文件的目录名(本例中为public)是不会出现在URL中的http://127.0.0.1/images/img0.jpghttp://127.0.0.1/html/index.htmlhttp://127.0.0.1/css/index.csshttp://127.0.0.1/js/index.js
app.use(express.static('public'))
  • 托管多个静态资源目录
app.use(express.static('public'))
app.use(express.static('files'))
......
  • 挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,可以使用下面的代码http://127.0.0.1/public/images/img0.jpghttp://127.0.0.1/public/html/index.htmlhttp://127.0.0.1/public/css/index.csshttp://127.0.0.1/public/js/index.js
app.use('public', express.static('public'))

nodemon

  • 为什么要使用 nodemon
在编写调试 Node.js 项目的时候,如果修改了项目的代码,则需要频繁的手动 close 掉,然后再重新启动,非常繁琐为了解决这个问题,我们可以使用 nodemon 这个工具,它能够监听项目文件的变动,当代码修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试
  • 安装 nodemon
在终端中运行下面命令,即可将 nodemon 安装为全局可用的工具npm install -g nodemon
  • 使用 nodemon
nodemon 要执行的js文件的路径
例如:nodemon E:\Desktop\jsProject\test.js

路由

直接挂载路由

const express = require('express')
const app = express()// 直接在app上挂载路由
app.get('/', (req, res) => { res.send('this is get') })
app.post('/', (req, res) => { res.send('this is post') })app.listen(80, () => {console.log('Server is running at http://127.0.0.1/')
})

模块化的方式,挂载路由,实现解耦合

  • 文件名:routerModule
// 这是路由模块// 1. 导入 express 模块
const express = require('express')// 2. 创建路由对象
const router = express.Router()// 3. 挂载具体的路由
router.get('/list', (req, res) => { res.send('Get user list.') })
router.post('/add', (req, res) => { res.send('Add new user.') })// 4. 向外导出路由对象
module.exports = router
  • 文件名:myApplication
// 这是 app (application-应用)// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 导入路由模块
const router = require('./routerModule')// 5. "注册"路由模块
app.use('/user', router)
// app.use(express.static('public'))
// 注意:app.use() 函数的作用,就是用来"注册"全局中间件// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })

中间键

  • 基本认识
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 定义一个中间件函数
const mw = (req, res, next) => {console.log('这是最简单的中间件函数')next()      // 把流转关系,转交给下一个中间件或路由
}// 5. 将 mw 注册为全局生效的中间件
// 当注册成功后,用户每次请求服务时,都会先执行中间键函数里面的内容,再执行路由函数里面的内容
app.use(mw)// 6. 创建路由
app.get('/', (req, res) => { res.send('home page') })
app.get('/user', (req, res) => { res.send('user page') })// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 体验它的作用
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 注册全局中间件
app.use((req, res, next) => {// 获取请求到达服务器的时间const time = Date.now()// 为req对象,挂载自定义属性,从而把时间共享给后面的所有路由req.startTime = timenext()
})// 5. 创建路由
app.get('/', (req, res) => { res.send('home page ' + req.startTime) })
app.get('/user', (req, res) => { res.send('user page ' + req.startTime) })// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 定义多个全局中间件
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 注册全局中间件
// 4.1 注册第一个中间件
app.use((req, res, next) => {console.log('调用了第1个中间件')next()
})// 4.2 注册第二个中间件
app.use((req, res, next) => {console.log('调用了第2个中间件')next()
})// 4.3 注册第三个中间件
app.use((req, res, next) => {console.log('调用了第3个中间件')next()
})// 5. 创建路由
app.get('/user', (req, res) => { res.send('user page') })// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 局部生效的中间件
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 定义一个中间件函数
const mw = (req, res, next) => {console.log('调用了局部生效的中间件')next()
}// 5. 创建路由
app.get('/', mw, (req, res) => { res.send('home page') })  // 使用了局部中间件
app.get('/user', (req, res) => { res.send('user page') })   // 没有使用局部中间件// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • 定义多个局部中间件
// 以下两种写法是等效的
app.get('/', mw1, mw2, (req, res)=>{ res.send('home page') })
app.get('/', [mw1, mw2], (req, res)=>{ res.send('home page') })
  • 错误级别的中间件
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4. 定义路由
app.get('/', (req, res) => {throw new Error('The Error made by People.')     // 人为制造错误res.send('home page')   // 此处无法执行
})// 5. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {console.log('警告!服务器发生了错误。\n错误信息:' + err.message)res.send('尊敬的用户,很抱歉,服务器出错了~')
})	// 错误级别的中间件一定要放在所有路由之后// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
  • express 内置的中间件
自从 Express 4.16.0 版本开始,Express 内置了3个常用的中间件,极大的提高了 Express 项目的开发效率和体验1. express.static 快速托管静态资源的内置中间件,例如:HTML文件、CSS样式、图片等2. express.json 解析 JSON 格式的请求体数据 (4.16.0+ 版本可用)3. express.urlencoded 解析 URL-encoded 格式的请求体数据 (4.16.0+ 版本可用)
// 1. 导入 express 模块
const express = require('express')// 2. 创建应用
const app = express()// 4.1 通过express.json()这个中间件,解析表单中的JSON
app.use(express.json())
// 4.2 通过express.urlencoded()这个中间件,来解析表单中的url-encoded格式的数据
app.use(express.urlencoded({ extended: false }))// 5. 创建路由
app.post('/user', (req, res) => {// 使用 req.body 属性,可用接收客户端发送过来的请求体数据// 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefinedconsole.log(req.body)res.send('ok')
})app.post('/book', (req, res) => {// 在服务器端,可用通过 req.body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据console.log(req.body)res.send('ok')
})// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })

跨域访问

  • 下载并安装 cors 中间件
npm i cors@2.8.5
  • 使用 cors 中间件
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())

跨域资源共享

点击链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

mysql

基本内容

  • 安装
npm install mysql
  • 配置
// 1. 导入 mysql 模块
const mysql = require('mysql')// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({host: '127.0.0.1',      // 数据库的IP地址user: 'root',           // 登录数据库的账号password: 'admin123',   // 登录数据库的密码database: 'myDatabase'  // 指定要操作哪个数据库
})
  • 测试
// 检测 mysql 模块能否正常工作
db.query('select 1', (err, results) => {if (err) return console.log(err.message)    // mysql 模块工作期间报错了console.log(results)    // 能够成功的执行SQL语句
})

查询与插入数据

  • 查询数据
// 1. 导入 mysql 模块
const mysql = require('mysql')// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({host: '127.0.0.1',      // 数据库的IP地址user: 'root',           // 登录数据库的账号password: 'admin123',   // 登录数据库的密码database: 'myDatabase'  // 指定要操作哪个数据库
})// 3. 查询 users 表中所有的数据
const sqlStr = 'select * from users'
db.query(sqlStr, (err, results) => {// 查询数据失败if (err) return console.log(err.message)// 查询数据成功console.log(results)
})
  • 插入数据
// 1. 导入 mysql 模块
const mysql = require('mysql')// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({host: '127.0.0.1',      // 数据库的IP地址user: 'root',           // 登录数据库的账号password: 'admin123',   // 登录数据库的密码database: 'myDatabase'  // 指定要操作哪个数据库
})// 3. 向 users 表中插入数据
const user = { username: 'Jack', password: '123456' }
const sqlStr = 'insert into users set ?'
db.query(sqlStr, user, (err, results) => {if (err) return console.log(err.message)    // 执行SQL语句失败了if (results.affectedRows === 1) { console.log('插入数据成功!') }
})

更新与删除数据

  • 更新数据
// 1. 导入 mysql 模块
const mysql = require('mysql')// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({host: '127.0.0.1',      // 数据库的IP地址user: 'root',           // 登录数据库的账号password: 'admin123',   // 登录数据库的密码database: 'myDatabase'  // 指定要操作哪个数据库
})// 3. 更新 users 表中数据
const user = { id: 6, username: 'Jack', password: '123456' }
const sqlStr = 'update users set ? where id=?'
db.query(sqlStr, [user, user.id], (err, results) => {if (err) return console.log(err.message)    // 执行SQL语句失败了if (results.affectedRows === 1) { console.log('更新数据成功!') }
})
  • 删除数据
// 1. 导入 mysql 模块
const mysql = require('mysql')// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({host: '127.0.0.1',      // 数据库的IP地址user: 'root',           // 登录数据库的账号password: 'admin123',   // 登录数据库的密码database: 'myDatabase'  // 指定要操作哪个数据库
})// 3. 删除 users 表中的数据(删除id=5的用户数据)
const sqlStr = 'delete from users where id=?'
db.query(sqlStr, 5, (err, results) => {if (err) return console.log(err.message)    // 执行SQL语句失败了if (results.affectedRows === 1) { console.log('删除数据成功!') }
})
标记删除法:(扩展)使用 delete 语句,是真的会把数据从表中抹除掉。为了保险起见,推荐使用标记删除法,来模拟删除的动作。什么是标记删除法呢?就是在创建表的时候,提前建立好一个 status 状态字段,用来表示当前这条数据是否被删除掉,当我们使用标记删除法后,哪怕用户删除数据了,有一天用户需要找回来,也能找到这条数据。

Web 开发模式

目前主流的开发模式有两种

  • 基于服务端渲染的传统 Web 开发模式
  • 基于前后端分离的新型 Web 开发模式

服务端渲染

概念

服务端发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用Ajax这样的技术额外请求页面的数据。

优点

  • 前端耗时少(因为服务器端负责动态生成HTML内容,浏览器只需要直接渲染即可,尤其是移动端,更省电)
  • 有利于SEO(因为服务器端响应的是完整的HTML页面内容,所以爬虫更容易获取信息,更有利于SEO)

缺点

  • 占用服务器资源(服务器端完成HTML页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力)
  • 不利于前后端分离,开发效率低(使用服务器端渲染,则无法分工合作,尤其对于前端项目复杂度高的项目,不利于项目高效开发)

前后端分离

概念

前后端分离的开发模式,依赖于Ajax技术的广泛应用。简而言之,前后端分离的Web开发模式,就是后端只负责提供API接口,前端使用Ajax调用接口的开发模式。

优点

  • 开发体验好。(前端专注于 UI 页面的开发,后端专注于 API 的开发,而且前端有更多的选择性)
  • 用户体验好。(Ajax 技术的广泛应用,极大的提高了用户的体验 ,可以轻松实现页面的局部刷新)
  • 减轻了服务器的压力(页面最终是在每个用户的浏览器中生成的)

缺点

  • 不利于 SEO (因为完整的HTML页面需要在客户端动态拼接完成,所以爬虫难以爬取页面的有效信息)
解决方案:利用 Vue、React 等前端框架的 SSR (server side render) 技术能够很好的解决 SEO 问题

身份认证

基本认识

介绍:身份认证(Authoritarian)又称"身份验证"、"鉴权",它是指通过一定的手段,完成对用户身份的确认例如:手机验证码登录、邮箱密码登录、二维码登录等
不同开发模式下的身份认证1.服务端渲染推荐使用 Session 认证机制2.前后端分离推荐使用 JWT 认证机制

Session 认证机制

Cookie

  • HTTP 无状态性质
HTTP 协议的无状态性质,指的是客户端每次的 HTTP 请求都是独立的,连续多个请求之前没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态
  • 突破 HTTP 无状态的限制
使用 Cookie 可以弥补 HTTP 无法记录状态的问题
  • 什么是 Cookie
Cookie 是存储在用户浏览器中的一段不超过 4KB 的字符串
它由一个名称(Name)、一个值(value)和其他几个用于控罪 Cookie 有效期、安全性、使用范围的可选属性组成
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器
  • Cookie 的几大特性
1.自动发送
2.域名独立
3.过期时限
4.4KB限制
  • Cookie 在身份认证中的作用
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验证客户端的身份
  • Cookie 不具有安全性
由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过 Cookie 的形式发送给浏览器

Session

  • Session = Cookie + 认证
  • Session 的工作原理

在这里插入图片描述

实现 Session 认证

  • 安装 express-session 中间件
npm install express-session
  • 配置 express-session 中间件
// 1. 导入 session 中间件
const session = require('express-session')// 2. 配置 Session 中间件
app.use(session({secret: 'keyboard cat',     // secret 属性的值可以为任意字符串resave: false,              // 固定写法saveUninitialized: true     // 固定写法
}))
  • session 操作
当 express-session 中间件配置成功后,即可通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息
// 1. 创建服务器实例
const express = require('express')
const app = express()// 3. 配置 express-session 中间件
const session = require('express-session')
app.use(session({secret: 'keyboard cat',     // secret 属性的值可以为任意字符串resave: false,              // 固定写法saveUninitialized: true     // 固定写法
}))// 4. 创建路由// 4.1 登录接口
app.post('api/login', (req, res) => {// 判断用户提交的登录信息是否正确if (req.body.username !== 'admin' || req.body.password !== '123456') {return res.send({ status: 1, msg: '登录失败' })}req.session.user = req.body     // 将用户的信息,存储到 Session 中req.session.isLogin = true      // 将用户的登录状态,存储到 Session 中req.send({ status: 0, msg: '登录成功' })
})// 4.2 获取用户姓名的接口
app.get('/api/username', (req, res) => {// 从 Session 中获取用户的名称,响应给客户端if (!req.session.isLogin) {return res.send({ status: 1, msg: 'fail' })}res.send({status: 0,msg: 'success',username: req.session.user.username})
})// 4.3 退出登录的接口
app.post('/api/logout', (req, res) => {// 清空当前客户端对应的 Session 信息req.session.destory()res.send({status: 0,msg: '退出登录成功'})
})// 2. 启动服务器
app.listen(80, (req, res) => {console.log('Server is running at http://127.0.0.1/')
})
  • Session 认证的局限性
Session 认证机制需要配合 Cookie 才能实现。
由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口时,需要做很多额外配置,才能实现跨域 Session 认证
当前端请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制
当前端需要跨域请求后端接口的时候,不推荐使用 Session 身份认证机制,推荐使用 JWT 认证机制

JWT 认证机制

简介

  • JWT(JSON Web Token)是目前最流行的跨域认证解决方案
  • 工作原理

在这里插入图片描述

  • **JWT **通常由三部分组成
Header		(头部)
Payload		(有效载荷)
Signature	(签名)
三者之间使用英文的"."进行分隔
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串
Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性
  • JWT 的使用方式
客户端收到服务器返回的 JWT 之后,通常会将它存储在 localStorage 或 sessionStorage 中
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证
推荐的做法是把 JWT 放在 HTTP 请求头的 Authoritarian 字段中
格式如下:
Authorization: Bearer <token>

实现 JWT 认证

  • 安装 JWT 相关的包
jsonwebtoken 用于生成 JWT 字符串
express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
npm install jsonwebtoken express-jwt
  • 使用 JWT
// 1. 创建服务器实例
const express = require('express')
const app = express()// 3. 使用 JWT
// 3.1 安装并导入 JWT 相关的两个包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')// 3.2 设置 secret 密钥,保证 JWT 字符串的安全
const secretKey = 'text use ^_^'// 3.4 将 JWT 字符串解析还原成 JSON 对象
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
const mw = expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] })
app.use(mw)     // 将这个中间件注册为全局中间件// 获取用户信息的接口
app.get('/admin/getInfo', (req, res) => {// 3.5 使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端console.log(req.user)res.send({status: 200,message: "获取用户信息成功",data: req.user})
})// 登录接口
app.post('api/login', (req, res) => {const userinfo = req.body// 登录失败if (userinfo.username !== 'admin' || userinfo.password !== '123456') {return res.send({ status: 400, message: '登录失败' })}// 登录成功// 3.3 登录成功后,生成 JWT 字符串,并通过 token 属性发送给客户端// 参数1:用户的信息对象// 参数2:加密的密钥// 参数3:配置对象,可以配置当前的 token 的有效期const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '60s' })req.send({ status: 200, message: '登录成功', token: tokenStr })
})// 3.6 使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res) => {// 判断错误是否为 token 解析失败导致if (err.name === 'UnauthorizedError') return res.send({ status: 401, message: '无效的token' })res.send({ status: 500, message: '未知的错误' })
})// 2. 启动服务器
app.listen(80, (req, res) => {console.log('Server is running at http://127.0.0.1/')
})

相关文章:

  • visual studio注册码
  • 20240202在WIN10下使用whisper.cpp
  • mysql入门到精通005-基础篇-约束
  • 大规模机器学习简介
  • Netty源码系列 之 EventLoop run()方法 源码
  • Swift Combine 发布者订阅者操作者 从入门到精通二
  • 蓝桥杯嵌入式第8届真题(完成) STM32G431
  • yt-dlp快速上手
  • vim最简单命令学习
  • Python脚本之操作Elasticsearch【二】
  • C# async/await的使用
  • kubernetes部署nacos2.3.0
  • 网站服务器中毒或是被入侵该怎么办?
  • Leetcode的AC指南 —— 栈与队列 :1047.删除字符串中的所有相邻重复项
  • YUM | 包安装 | 管理
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • canvas 高仿 Apple Watch 表盘
  • js ES6 求数组的交集,并集,还有差集
  • Koa2 之文件上传下载
  • Laravel 中的一个后期静态绑定
  • Making An Indicator With Pure CSS
  • python学习笔记 - ThreadLocal
  • vuex 笔记整理
  • Vue官网教程学习过程中值得记录的一些事情
  • Web标准制定过程
  • 爱情 北京女病人
  • 编写高质量JavaScript代码之并发
  • 成为一名优秀的Developer的书单
  • 大快搜索数据爬虫技术实例安装教学篇
  • 服务器之间,相同帐号,实现免密钥登录
  • 今年的LC3大会没了?
  • 开源地图数据可视化库——mapnik
  • 巧用 TypeScript (一)
  • 微信小程序设置上一页数据
  • 学习HTTP相关知识笔记
  • 一道面试题引发的“血案”
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • nb
  • scrapy中间件源码分析及常用中间件大全
  • # 数据结构
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (16)Reactor的测试——响应式Spring的道法术器
  • (二)斐波那契Fabonacci函数
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (四)Controller接口控制器详解(三)
  • (一)Linux+Windows下安装ffmpeg
  • (一)u-boot-nand.bin的下载
  • (一)WLAN定义和基本架构转
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .NET委托:一个关于C#的睡前故事
  • .NET正则基础之——正则委托