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/')
})

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

链式二叉树(3)

目录 Main函数 ​ 二叉树第K层的节点个数 整体思路 分析理解 注意事项 二叉树查找值为x的节点 整体思路 分析理解 注意事项 Main函数 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<math.h&g…

AI新宠Arc浏览器真可以取代Chrome吗?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

蓝桥杯备战——13.PCF8591芯片的使用

目录 1.芯片简介2.读写时序3.控制字4.代码封装库5.原理图分析6.使用示例 1.芯片简介 截取自NXP的PCF8591芯片数据手册&#xff0c;我把重点关注部分划出来了&#xff0c;请务必自行阅读一遍数据手册&#xff01; 2.读写时序 ①器件地址&#xff1a; Bit0决定是读还是写操作&…

零基础学编程系列,从入门到精通,中文编程开发语言工具下载,编程构件容器件之控制面板构件用法

零基础学编程系列&#xff0c;从入门到精通&#xff0c;中文编程开发语言工具下载&#xff0c;编程构件容器件之控制面板构件用法 一、前言 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例源码文件下载可以点击最下方官网卡片——软件下载…

[SWPUCTF 2021 新生赛]caidao

看类型判断远程执行漏洞 我这边用蚁剑连接 查看/目录看到flag

神经网络 | 基于多种神经网络模型的轴承故障检测

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要源自《第二届全国技能大赛智能制造工程技术项目比赛试题&#xff08;样题&#xff09; 模块 E 工业大数据与人工智能应用》&#xff0c;基于给出的已知轴承状态的振动信号样本&#xff0c;对数据进行分析&#xff0c;建…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Menu组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Menu组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Menu组件 TextClock组件通过文本将当前系统时间显示在设备上。支持不同时区的时间…

js中执行上下文和执行栈是什么

文章目录 一、执行上下文二、生命周期创建阶段This Binding词法环境变量环境 执行阶段回收阶段 二、执行栈参考文献 一、执行上下文 简单的来说&#xff0c;执行上下文是一种对Javascript代码执行环境的抽象概念&#xff0c;也就是说只要有Javascript代码运行&#xff0c;那么…

onnx转换为rknn置信度大于1,图像出现乱框问题解决

前言 环境介绍&#xff1a; 1.编译环境 Ubuntu 18.04.5 LTS 2.RKNN版本 py3.8-rknn2-1.4.0 3.单板 迅为itop-3568开发板 一、现象 采用yolov5训练并将pt转换为onnx&#xff0c;再将onnx采用py3.8-rknn2-1.4.0推理转换为rknn出现置信度大于1&#xff0c;并且图像乱框问题…

FPGA项目(16)——基于FPGA的音乐演奏电路

1.设计要求 能在实验箱上&#xff0c;循环播放一段音乐。&#xff08;需要源码的直接看最后一节&#xff09; 2.设计原理 组成乐曲的每个音符的发音频率值及其持续的时间是乐曲能连续演奏所需要的两个基本要素&#xff0c;问题是如何来获取这两个要素所对应的数值以及通过纯硬件…

sentinel的Context创建流程分析

sentinel入门 功能 限流&#xff1a;通过限制请求速率、并发数或者用户数量来控制系统的流量&#xff0c;防止系统因为流量过大而崩溃或无响应的情况发生。 熔断&#xff1a;在系统出现故障或异常时将故障节点从系统中断开&#xff0c;从而保证系统的可用性。 降级&#xf…

Netflix Mac(奈飞mac客户端) v2.13.0激活版

Clicker for Netflix Mac版是一款适用于Mac的最佳独立Netflix播放器&#xff0c;具有直接从从Dock启动Netflix&#xff0c;从触摸栏控制Netflix&#xff0c;支持画中画等多种功能&#xff0c;让你拥有更好的观看体验。 软件特色 •直接从Dock启动Netflix •从触摸栏控制Netflix…