# node.js **Repository Path**: guibinbin/node.js ## Basic Information - **Project Name**: node.js - **Description**: node.js - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-08-25 - **Last Updated**: 2024-11-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Node.js ## Node架构 ![1692945017997](image/README/1692945017997.png) ### Natives modules - 当前层由 JS 实现 - 提供应用程序可直接调用库 - JS 无法直接操作底层硬件设置 ### Builtin modules ### 底层 - V8: 执行 JS 代码,提供桥梁接口 - Libuv: 事件循环、事件队列、异常IO - 第三方模块: zlib、 http 等 ## Node.js 服务端语言 ## Node.js 异步IO ![1692945883998](image/README/1692945883998.png) - IO 是应用程序的瓶颈所在 - 异步IO 提高性能无需原地等待结果返回 - IO 操作属于操作系统级别,平台都有对应实现 - Nodejs 单线程配合事件驱动架构及 libuv实现 异步IO ## Nodejs 的事件驱动架构 > 事件驱动架构 是 软件开发中的通用模式 ## Nodejs 单线程 - Nodejs 主线程 是 单线程 - 不适合处理 CPU 密集型计算 ## Nodejs 应用场景 (适合 IO 密集型任务) - IO 密集型高并发请求 ![1692946917506](image/README/1692946917506.png) - 操作数据库提供 API 服务 ## Nodejs 注意点 - Nodejs 中不能使用 BOM 和 DOM 的 API,可以使用 console 和定时器 API - Nodejs 中顶级对象 global,也可以用globalThis 访问顶级对象 ## Buffer > Buffer 缓冲区,是一个类似 Array 的对象,用于表示固定长度的字节序列 - Buffer 就是一段固定长度的内存空间,用于处理二进制数据 ### 特点 - 大小固定 - 性能较好,直接对内存进行操作 - 每个元素大小为 1 字节(byte) ### 创建 Buffer ```javascript // 创建 Buffer // 1. alloc // 先会清空内存 let buf = Buffer.alloc(10); // console.log(buf); // // 2. allocUnsafe // 生成的数据可能有之前的数据,不会先清空内存 let buf1 = Buffer.allocUnsafe(10); console.log(buf1); // // 3. from let buf3 = Buffer.from('hello') console.log(buf3); // ``` ### Buffer 与 字符串的转换 ```javascript // Buffer 与 字符串的转化 let buf = Buffer.from([105,108,111,118,101,121,111,117]) console.log(buf.toString()) // utf-8 // iloveyou ``` ### Buffer 元素读写 ```javascript let buf = Buffer.from("hello") console.log(buf) buf[0] = 96 console.log(buf.toString()) // // `ello ``` ## fs 模块 - fs 模块可以实现与硬盘的交互 ```javascript const fs = require("fs") fs.writeFile('./zuoyouming.txt', '雷猴', err => { if (err) { console.log('写入失败') return } }) ``` ### fs 同步 与 异步 ### fs 追加写入(适合写入少的场景) ```javascript const fs = require("fs") fs.appendFile("./zuoyouming.txt", "xiaohuozhi",(err)=>{ if(err){ console.log("Failed to append") return } console.log("Success") }) appendFileSync ``` ### fs 流式写入 (适合写入频繁的场景) ```javascript const fs = require('fs'); const ws = fs.createWriteStream('first.txt') ws.write('nihao\r\n') ws.write('nihao\r\n') ws.write('nihao\r\n') ws.write('nihao\r\n') ``` ### fs 读取文件 ```javascript const fs = require('fs'); fs.readFile('./first.txt', 'utf-8',(err,data)=>{ if(err){ console.log(err); return } console.log(data.toString()) }) // const fs = require('fs'); let data = fs.readFileSync('./first.txt') console.log(data.toString()); ``` ### fs 流式读取 (一块一块读取的) ```javascript const fs = require('fs'); const rs = fs.createReadStream('./1.mp3') rs.on('data', chunk => { console.log(chunk) }) // end 事件 会 默认执行 // rs.on('end', ()=>{ // console.log('end') // }) ``` ### fs 复制文件 ```javascript /** * 需求: * 复制文件 */ // 使用 readFile 和 writeFile const fs = require('fs'); fs.readFile('./1.mp3', (err, data)=>{ if(err){ console.log('Error reading') return } fs.writeFile('./read1.mp3', data, (err)=>{ if(err){ console.log('Error'); return } }) }) // 使用 流读取写入 const fs = require('fs'); const rs = fs.createReadStream('./1.mp3') const ws = fs.createWriteStream('./c1.mp3') rs.on('data', chunk => { console.log(chunk) if(!chunk){ return } ws.write(chunk) }) ``` ### fs 文件移动与重命名 ```javascript // 文件重命名 const fs = require('fs'); // 异步 fs.rename('./1.mp3', './2.mp3', err =>{ if(err){ console.log(err); return; } }) // 同步 renameSync() // 文件移动 const fs = require('fs'); fs.rename('./2.mp3', './file/2.mp3', err =>{ if(err){ console.log(err); return; } }) ``` ### fs 文件删除 ```javascript const fs = require('fs'); // 异步 fs.unlink('./file/2.mp3', err =>{ if(err){ console.log('Unlink Error') return } }) // 同步 unlinkSync(path); // 方式二 const fs = require('fs'); fs.rm('./file/2.mp3', err =>{ if(err){ console.log('Unlink Error') return } }) rmSync() ``` ### fs 文件夹操作 ```javascript const fs = require('fs'); fs.mkdir('./new', err => { if(err){ console.log("Error mkdir") return } console.log('Sucessfully') }) fs.mkdirSync(); // 2. 递归创建文件夹 const fs = require('fs'); // 递归创建 fs.mkdir('./new/a/b/c', {recursive: true}, err => { if(err){ console.log("Error mkdir") return } console.log('Sucessfully') }) // 3. 读取文件夹 const fs = require('fs'); // 读取文件夹 fs.readdir('./', (err,data) => { if (err) { console.log('error') return } console.log(data) }) // 4. 删除文件夹 const fs = require('fs'); fs.rmdir('./new/a', err =>{ if(err){ console.log('error') return } console.log('Successfully') }) // 递归删除文件夹 const fs = require('fs'); fs.rmdir('./new', {recursive: true}, err =>{ if(err){ console.log('error') return } console.log('Successfully') }) ``` ### fs 查看资源状态 ```javascript const fs = require('fs'); fs.stat('./first.txt', (err, data) => { if(err){ console.log('Error') return } console.log(data) }) let data = fs.statSync('./first.txt') console.log(data) ``` #### fs 路径 - 相对路径 - 绝对路径 #### fs 相对路径的 Bug ```javascript // 相对路径的参照物: 命令行的工作目录 ``` ### __dirname - 表示这个文件的所在目录的绝对路径 ### fs 批量重命名 ```javascript const fs = require('fs'); // 批量重命名 const files = fs.readdirSync('./new'); // console.log(files) let dir = `${__dirname}/new/` let i = 0 files.forEach(item => { console.log(item) let newName = (item.split('.')[0] = i++) + '.' + item.split('.')[1] reName(dir, item, newName) }) // 重命名 function reName(dir, filename, Name){ fs.rename(`${dir}/${filename}`, `${dir}/${Name}`, err => { if(err) throw err console.log('Successfully') }) } ``` ## path 模块 ![1692954073677](image/README/1692954073677.png) ## HTTP 请求报文 ### 请求行 > GET https://www.baidu.com/ HTTP/1.1 - 请求方法 - ![1692965259669](image/README/1692965259669.png) - URL - ![1692965382138](image/README/1692965382138.png) - 协议名 - 主机名 - 端口号 - 路径 - 查询字符串 - HTTP 版本号 ### 请求头 ### 请求体 ### HTTP 响应报文 ![1692965742094](image/README/1692965742094.png) ![1692965756232](image/README/1692965756232.png) #### 响应头 ## http 模块 ```javascript const http = require('http') const server = http.createServer((req, res) => { res.end("hello world") }); server.listen(8088, ()=>{ console.log("listening on 8088") }); ``` ### HTTP 注意事项 ![1692966337174](image/README/1692966337174.png) ### HTTP 响应文件内容 ```javascript const http = require('http') const fs = require('fs') const server = http.createServer((req, res) => { let html = fs.readFileSync('./file/index.html') res.end(html) }); server.listen(8088, ()=>{ console.log("listening on 8088") }); ``` ## 网页资源加载基本过程 - html - css - js - 图片等 ### HTTP 响应练习扩展 ```javascript // 识别不同请求返回对应文件 const http = require('http') const fs = require('fs') const server = http.createServer((req, res) => { // 获取 请求路径 let {pathname} = new URL(req.url, 'http://localhost:8088') console.log(pathname) let path; if(pathname === '/'){ path = __dirname + '\\file\\index.html' }else if(pathname === '/index.css'){ path = __dirname + '\\file\\index.css' }else if(pathname === '/index.js'){ path = __dirname + '\\file\\index.js' }else{ res.end('404') } let result = fs.readFileSync(path) res.end(result) }); server.listen(8088, ()=>{ console.log("listening on 8088") }); ``` ## 静态资源 和 动态资源 ### 搭建静态资源服务 ### 静态资源目录网站资源目录 ![1692969777745](image/README/1692969777745.png) ## 网页中的 URL ### 绝对路径 ![1692970056913](image/README/1692970056913.png) ### 相对路径 ![1692970334339](image/README/1692970334339.png) ### 设置 MIME 类型 > 媒体类型 Multipurpose Internet Mail Extensions, 用来表示文档、文件 或 字节流的性质和格式 ![1693014069242](image/README/1693014069242.png) ### 解决乱码问题 - 1. 设置响应头 (优先级更高) - 2. 设置 meta 标签 ### 完善资源请求错误处理 ### GET 和 POST 请求场景 ![1693015209237](image/README/1693015209237.png) #### 请求区别 ![1693015235768](image/README/1693015235768.png) ## Node 模块化 > 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程 叫做 模块化 ### 好处 - 防止命名冲突 - 高复用性 - 高维护性 ### 模块暴露数据 ![1693015476515](image/README/1693015476515.png) #### 注意 ```javascript // require 返回的结果是 module.exports 的值 ``` ### Nodejs 导入模块 ```javascript // require ``` #### 注意 ![1693015687763](image/README/1693015687763.png) #### 导入文件夹的情况 如果导入的路径是一个文件夹,会首先检测该文件夹下的package.json 中 main 的属性指向的文件 如果 main 属性不存在,或 package.json 不存在,则会检测文件夹 index.js 和 index.json 如果还是没有,就报错 ##### 文件结构 - pack - module - app.js - package.json - main.js ```javascript // package.json { "main": "./app.js" } ``` ```javascript // app.js module.exports = '我是一个模块' ``` ```javascript // main.js // 导入 const m = require('./module') console.log(m) // 我是一个模块 ``` ### require 导入模块基本流程 ![1693016236638](image/README/1693016236638.png) #### require 伪代码 ```javascript function require(file){ // 1. 将相对路径转换为绝对路径,定位目标文件 let absolutePath = path.resolve(__dirname, file) // 2. 缓存检测 if(caches[absolutePath]){ return caches[absolutePath] } // 3. 读取文件的代码 let code = fs.readFileSync(absolutePath).toString() // 4. 包裹为一个函数 let module = {} let exports = module.exports = {} (function(exports, require, module, __filename, __dirname){ const test = { name: "nihao" } module.exports = test console.log(arguments.callee.toString()) })(exports, require, module, __filename, __dirname) // 5. 缓存结果 caches[absolutePath] = module.exports // 6. 返回缓存结果 return module.exports; } ``` ##### 注意 ```javascript const m = require('./m.js') const m1 = require('./m.js') // 结果只输出一次,因为有缓存 ``` ## CommonJS 规范 > Nodejs 是实现了 CommonJS 模块化规范。 ## npm 初始化包 ```javascript npm init 每一个包都要有 package.json 文件 包 的配置文件 ``` ![1693017347130](image/README/1693017347130.png) ## npm 搜索包 ![1693017390207](image/README/1693017390207.png) ## 开发依赖 和 生产依赖 ![1693017446824](image/README/1693017446824.png) ```javascript { "name": "npmtest", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "guibin", "license": "ISC", // 生产环境 npm i -S jquery "dependencies": { "jquery": "^3.7.0" }, // 开发环境 npm i -D less "devDependencies": { "less": "^4.2.0" } } ``` ## npm 全局安装 ![1693017822810](image/README/1693017822810.png) ### 修改 windows 执行策略 - ![1693017920606](image/README/1693017920606.png) - ![1693017940435](image/README/1693017940435.png) - 选 A ## 环境变量 Path ## npm 安装指定版本包 ![1693018076800](image/README/1693018076800.png) ## npm 配置命令别名 ![1693018158993](image/README/1693018158993.png) ## nvm 介绍 使用 > 用于管理 node 的版本管理工具,方便切换版本 > ![1693018241974](image/README/1693018241974.png) > [nvm 安装、卸载与使用(详细步骤)](https://juejin.cn/post/7000652162950758431) # express 框架 ## express 路由 > 路由确定了应用程序如何响应客户端对特定端点的请求 - 一个路由的组成有 `请求方法`, `路径` 和 `回调函数` 组成 ### 路由的使用 ```javascript // 语法 app.(path, callback) // 1. 导入 express const express = require('express') // 2. 创建应用对象 const app = express() // get 请求 app.get('/home', (req, res)=>{ res.end('hello') }) // post 请求 app.post('/home', (req, res)=>{ res.end('home') }); // all 所有,只要是 /test 这个路径就匹配 app.all('/test', (req, res)=>{}) // * 通配符 app.all('*', (req, res)=>{}) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) ``` ## 获取请求报文参数 ```javascript // 获取请求的路由规则 app.get('/request', (req, res) => { // 1. 获取报文的方式与原生 HTTP 获取方式是兼容的 console.log(req.method) console.log(req.url) console.log(req.httpVersion) console.log(req.headers) // express 独有的获取报文方式 // 获取查询字符串 console.log(req.query) // 获取指定的请求头 console.log(req.get('host')) res.send('请求报文参数获取') }) ``` ## 获取路由参数 ![1693036357679](image/README/1693036357679.png) ```javascript // 获取请求的路由规则 app.get('/:id.html', (req, res) => { console.log(req.params.id) res.end('hello') }) ``` ### 路由参数练习 > 通过请求找歌手数据 - package - Data - data.json - index.js - package.json - package-lock.json ```javascript // index.js // 1. 导入 express const express = require('express') const fs = require('fs') const {singers} = require('./Data/data.json') // 2. 创建应用对象 const app = express() // 获取请求的路由规则 app.get('/singer/:id.html', (req, res) => { let {id} = req.params let result = singers.find(item => { if(item.singer_name === id) { res.setHeader('Content-Type', 'application/json; charset=utf-8') res.end(JSON.stringify(item)) } }) }) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) // data.json { "singers":[ { "singer_name": "周杰伦", "singer_pic": "pic_周杰伦", "other_singer_name": "Jay_Chou" }, { "singer_name": "李荣浩", "singer_pic": "pic_李荣浩", "other_singer_name": "Jay_Chou" }, { "singer_name": "张杰", "singer_pic": "pic_张杰", "other_singer_name": "Jay_Chou" }, { "singer_name": "陈奕迅", "singer_pic": "pic_陈奕迅", "other_singer_name": "Eason" }, { "singer_name": "林俊杰", "singer_pic": "pic_林俊杰", "other_singer_name": "Jay_Chou" } ] } ``` ### 响应设置 ![1693038298729](image/README/1693038298729.png) ### express 中间件 ```javascript 1. 什么是中间件 本质: 回调函数 中间件函数 可以像路由回调一样访问 请求对象(request) 响应对象(response) 2. 中间件的作用 中间件的作用 就是 使用函数封装公共操作,简化代码 3. 中间件类型 - 全局中间件 - 路由中间件 ``` #### 定义全局中间件 ```javascript /** * 全局中间件 * 记录每个请求的 url 和 IP 地址 */ const express = require('express') const app = express() const fs = require('fs') const path = require('path') // 声明中间件函数 function recordMiddleware(req, res, next) { // 获取 url 和 ip let {url, ip} = req // 将信息保存在文件中 access.log fs.appendFileSync(path.resolve(__dirname, './access.log'),`${url} ${ip} \r\n`) // 调用 next next() } // 使用中间件函数 app.use(recordMiddleware) app.get('/home', (req, res) => { res.send('home') }) app.get('/admin', (req, res) => { res.send('admin') }) app.get('*', (req, res) => { res.send('*') }) app.listen(3000, (req, res) => { console.log('listening on port 3000') }) ``` #### 路由中间件实践 ```javascript /** * 路由中间件 * 针对 /admin /setting 的请求要求 URL 携带 code = 521 参数,如果没有携带提示 [暗号错误] */ const express = require('express') const app = express() const path = require('path') app.get('/home', (req, res) => { res.send('home') }) // 设置路由中间件 let checkCodeMiddleware = (req, res, next) =>{ // 判断 URL 中是否 code 参数为 521 if(req.query.code === '521'){ next() }else{ res.send('暗号错误') } } app.get('/admin', checkCodeMiddleware, (req, res) => { res.send('admin') }) app.get('/setting', checkCodeMiddleware, (req, res) => { res.send('setting') }) app.get('*', (req, res) => { res.send('*') }) app.listen(3000, (req, res) => { console.log('listening on port 3000') }) ``` #### 静态资源中间件 - package - public - css - index.css - index.html - index.js ```javascript // index.js // 1. 导入 express const express = require('express') // 2. 创建应用对象 const app = express() // 静态资源中间件设置 app.use(express.static(__dirname + '/public')) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) ``` ##### 注意点 ![1693053880458](image/README/1693053880458.png) ### 获取请求体数据 ![1693054075458](image/README/1693054075458.png) ![1693054090208](image/README/1693054090208.png) ```javascript /** * 搭建 HTTP 服务 * GET /login 显示表单网页 * POST /login 获取表单中的 [用户名] 和 [密码] */ // 1. 导入 express const express = require('express') const bodyParser = require('body-parser') // 2. 创建应用对象 const app = express() // 静态资源中间件设置 app.use(express.static(__dirname + '/public')) // 解析 JSON 格式的请求体的中间件 const jsonParser = bodyParser.json() // 解析 querystring 格式请求体的中间件 const urlencodedParser = bodyParser.urlencoded({extended:false}) app.get('/login', (req, res) => { res.sendFile(__dirname + '/public/login/login.html') }) app.post('/login', urlencodedParser, (req, res) => { console.log(req) let {username, password} = req.body console.log(username, password) res.send(`${username}: ${password}`) }) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) ``` ```javascript // login.html login

login

username

password

``` ### 防盗链 > 防止外部网站盗用网站资源 #### 防盗链实践 ```javascript /** * 图片防盗链 */ // 1. 导入 express const express = require('express') // 2. 创建应用对象 const app = express() // 声明中间件 app.use((req,res,next) => { // 检测请求头中的 referer 是否为 127.0.0.1 // 获取 referer let referer = req.get('referer') console.log(referer) if(referer){ // 实例化 let url = new URL(referer) let hostname = url.hostname if(hostname !== '127.0.0.1'){ res.status(404).send('

404

') return } } next() }) // 静态资源中间件设置 app.use(express.static(__dirname + '/public')) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) ``` ### 路由模块化 ```javascript // index.js // 1. 导入 express const express = require('express') const index = require('./public/routes/index') // 2. 创建应用对象 const app = express() app.use(index) // 静态资源中间件设置 app.use(express.static(__dirname + '/public')) // 4. 监听端口,启动服务 app.listen(3000, ()=>{ console.log('listening on port 3000') }) // routes/index.js const express = require('express'); const router = express.Router(); router.get('/', function (req, res){ res.send('home') }) router.get('/home', function (req, res){ res.send('home') }) router.get('/about', function (req, res){ res.send('about') }) module.exports = router; ``` ## 模板引擎(EJS) > 模板引擎是分离 用户界面 和 业务数据 的一种数据 ### 下载 ```javascript npm i ejs --save ``` ### ejs 初体验 ```javascript // 导入 ejs const ejs = require('ejs'); const fs = require('fs'); // 字符串 let china = '中国' let str = fs.readFileSync('./01_模板渲染.html').toString() // 使用 ejs 渲染 let result = ejs.render(str, {china:china}) console.log(result) ``` ```html Document

我爱你 <%= china %>

``` ### ejs 列表渲染 ```javascript // 导入 ejs const ejs = require('ejs'); let xiyou = ['孙悟空','唐僧','猪八戒','沙悟净'] // 使用 ejs 渲染 let result = ejs.render( `
    <% xiyou.forEach(item => { %>
  • <%= item %>
  • <% }) %>
` , {xiyou: xiyou}) console.log(result) ``` ### ejs 条件渲染 ```javascript // 导入 ejs const ejs = require('ejs'); let isLogin = false; // 使用 ejs 渲染 let result = ejs.render(` <% if(isLogin){ %> True <% } else { %> False <% } %>`, {isLogin: isLogin}) console.log(result) ``` ### express 中使用ejs ```javascript // index.js const express = require('express') const path = require('path') const app = express() // 1. 设置模板引擎 app.set('view engine', 'ejs') // 2. 设置模板文件存放位置 // 模板文件: 具有模板语法的内容的文件 app.set('views', path.resolve(__dirname, './views')) app.get('/', (req, res) => { let title = '你好啊啊啊' // res.render(模板的文件名,数据) res.render('home', {title}) }) app.listen(3000, ()=>{ console.log('listening on 3000') }) // home.ejs <%= title %>

测试

``` ### express-generator (生成器) > 通过应用生成器工具快速创建一个应用的骨架 ```javascript npm i -g express-generator ``` ### 文件上传报文 ```html
用户名:
头像:

``` #### formidable (处理文件请求) ## 接口的介绍 > 前后端通讯的桥梁 ### RESTful API ![1693360340334](image/README/1693360340334.png) ### json-server 工具 ![1693360320874](image/README/1693360320874.png) ### 会话控制 ![1693360215612](image/README/1693360215612.png) #### **Cookie** > cookie 是 HTTP 服务器发送到用户浏览器并保存在本地的一小块数据 - cookie 是保存在浏览器端的一小块数据 - cookie 是按照域名划分保存的 ##### 特点 浏览器向服务器发送请求时,会自动将 当前域名下 可用的 cookie 设置在请求头中,然后传递给服务器 ##### 浏览器操作 cookie 1. 禁用 cookie 2. 删除 cookie 3. 查看 cookie ##### express 设置 Cookie ```javascript const express = require('express') const cookieParser = require('cookie-parser') const app = express() app.use(cookieParser()) // 设置 cookie app.get('/set-cookie', (req,res)=>{ // res.cookie('name','zhansan') // 会在浏览器关闭时删除 res.cookie('name','zhansan', {maxAge: 60 * 1000}) // 会在浏览器关闭时删除 res.cookie('age',23) // 会在浏览器关闭时删除 res.send('home') }) // 删除 cookie app.get('/rm-cookie', (req,res)=>{ res.clearCookie('name') res.send('del Successfully') }) // 获取 cookie app.get('/get-cookie', (req,res)=>{ console.log(req.cookies) res.send('read') }) app.listen(3000, () => { console.log('listening on port 3000') }) ``` #### **Session** 1. session 是什么 是保存在 服务器端的一块数据, 保存当前访问用户的相关信息 2. 作用 实现会话控制,可以识别用户的身份,快速获取当前用户相关信息 3. 运行流程 ![1693365077588](image/README/1693365077588.png) ![1693365090224](image/README/1693365090224.png) ##### Session 中间件 ```javascript const express = require('express') const session = require('express-session') const MongoStore = require('connect-mongo') const app = express() app.use( session({ name: 'sid', // 设置 cookie 的 name 默认值是 connect.id secret: 'atguigu', // 参与加密的的字符串 (签名) // 是否为每次请求都设置一个 cookie 用来 存储session 的 id saveUninitialized: false, // 是否每次请求时都重新保存 session resave: false, store: MongoStore.create({ mongoUrl: 'mongodb://localhost:27017/', }), cookie: { // 开启后前端无法通过 JS 操作 httpOnly: true, // sessionId 的超时时间 maxAge: 1000 * 60 * 5, // 5 分钟 } }) ) app.get('/', (req,res)=>{ res.send('home') }) // 登录 app.get('/login', (req,res)=>{ // username password if(req.query.username === 'admin' && req.query.password === '123456'){ // 设置 session req.session.username = 'admin' req.session.uid = 'sdfsa' res.send('login success') }else{ res.send('login fail') } }) // session 读取 app.get('/cart', function(req, res){ if(req.session.username){ res.send('login success cart') }else{ res.send('还没登录...') } }) // session 销毁 app.get('/logout', (req, res) => { req.session.destroy(()=>{ res.send('logout success') }) }) app.listen(3000, () => { console.log('listening on port 3000') }) ``` ### Session 与 Cookie 的区别 ![1693381309497](image/README/1693381309497.png) ### CSRF 跨站请求伪造 ### Token ![1693381658296](image/README/1693381658296.png) ![1693381749479](image/README/1693381749479.png) #### JWT JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,可用于基于 token 的身份认证 ##### 实例 ```javascript // 导入 jwt const jwt = require('jsonwebtoken'); // // 创建 token // // let token = jwt.sign(用户数据,加密字符串,配置对象) // let token = jwt.sign({ // username: 'zhangsan', // }, 'jsonegg', { // expiresIn: 60 // 单位 s // }) // console.log(token) let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InpoYW5nc2FuIiwiaWF0IjoxNjkzMzgyMjU2LCJleHAiOjE2OTMzODIzMTZ9.rmvbhFJo3CXv4rjqSJUhis_L9vHiqk-2zJsV0xpdaAI' // 校验 token jwt.verify(token, 'jsonegg', (err, data)=>{ if(err){ console.log(err); }else{ console.log(data); } }) ``` - 校验成功 (解析数据) ![1693382376792](image/README/1693382376792.png) - 校验失败 (超时) ![1693382363768](image/README/1693382363768.png) # 前后端开发扩展介绍 ![1693382456672](image/README/1693382456672.png) # [分布式 和 cap 理论](./分布式node.md)