Node.js 学习

181 阅读7分钟

Node.js

Node架构

image.png

Natives modules

  • 当前层由 JS 实现
  • 提供应用程序可直接调用库
  • JS 无法直接操作底层硬件设置

Builtin modules

底层

  • V8: 执行 JS 代码,提供桥梁接口
  • Libuv: 事件循环、事件队列、异常IO
  • 第三方模块: zlib、 http 等

Node.js 服务端语言

Node.js 异步IO

image.png

  • IO 是应用程序的瓶颈所在
  • 异步IO 提高性能无需原地等待结果返回
  • IO 操作属于操作系统级别,平台都有对应实现
  • Nodejs 单线程配合事件驱动架构及 libuv实现 异步IO

Nodejs 的事件驱动架构

事件驱动架构 是 软件开发中的通用模式

Nodejs 单线程

  • Nodejs 主线程 是 单线程
  • 不适合处理 CPU 密集型计算

Nodejs 应用场景 (适合 IO 密集型任务)

  • IO 密集型高并发请求 image.png
  • 操作数据库提供 API 服务

Nodejs 注意点

  • Nodejs 中不能使用 BOM 和 DOM 的 API,可以使用 console 和定时器 API
  • Nodejs 中顶级对象 global,也可以用globalThis 访问顶级对象

Buffer

Buffer 缓冲区,是一个类似 Array 的对象,用于表示固定长度的字节序列

  • Buffer 就是一段固定长度的内存空间,用于处理二进制数据

特点

  • 大小固定
  • 性能较好,直接对内存进行操作
  • 每个元素大小为 1 字节(byte)

创建 Buffer

// 创建 Buffer
// 1. alloc // 先会清空内存
let buf = Buffer.alloc(10); // 
console.log(buf); 
// <Buffer 00 00 00 00 00 00 00 00 00 00>

// 2. allocUnsafe
// 生成的数据可能有之前的数据,不会先清空内存
let buf1 = Buffer.allocUnsafe(10);
console.log(buf1); 
// <Buffer 00 00 00 00 00 00 00 00 00 00>

// 3. from
let buf3 = Buffer.from('hello')
console.log(buf3);
// <Buffer 68 65 6c 6c 6f>

Buffer 与 字符串的转换

// Buffer 与 字符串的转化
let buf = Buffer.from([105,108,111,118,101,121,111,117])

console.log(buf.toString()) // utf-8
// iloveyou

Buffer 元素读写

let buf = Buffer.from("hello")

console.log(buf)
buf[0] = 96
console.log(buf.toString())
// <Buffer 68 65 6c 6c 6f>
// `ello

fs 模块

  • fs 模块可以实现与硬盘的交互
const fs = require("fs")

fs.writeFile('./zuoyouming.txt', '雷猴', err => {
    if (err) {
        console.log('写入失败')
        return
    }
})

fs 同步 与 异步

fs 追加写入(适合写入少的场景)

const fs = require("fs")

fs.appendFile("./zuoyouming.txt", "xiaohuozhi",(err)=>{
    if(err){
        console.log("Failed to append")
        return
    }
    console.log("Success")
})

appendFileSync

fs 流式写入 (适合写入频繁的场景)

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 读取文件

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 流式读取

(一块一块读取的)

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 复制文件

/**
 * 需求:   
 *     复制文件
 */

// 使用 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 文件移动与重命名

// 文件重命名
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 文件删除

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 文件夹操作

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 查看资源状态

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

// 相对路径的参照物:  命令行的工作目录

__dirname

  • 表示这个文件的所在目录的绝对路径

fs 批量重命名

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 模块

image.png

HTTP 请求报文

请求行

GET www.baidu.com/ HTTP/1.1

  • 请求方法

    • image.png
  • URL

    • image.png
    • 协议名
    • 主机名
    • 端口号
    • 路径
    • 查询字符串
  • HTTP 版本号

请求头

请求体

HTTP 响应报文

image.png

响应头

http 模块

const http = require('http')

const server = http.createServer((req, res) => {
    res.end("hello world")
});

server.listen(8088, ()=>{
    console.log("listening on 8088")
});

HTTP 注意事项

image.png

HTTP 响应文件内容

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 响应练习扩展

// 识别不同请求返回对应文件
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")
});

静态资源 和 动态资源

搭建静态资源服务

静态资源目录网站资源目录

image.png

网页中的 URL

绝对路径

image.png

相对路径

image.png

设置 MIME 类型

媒体类型 Multipurpose Internet Mail Extensions, 用来表示文档、文件 或 字节流的性质和格式

image.png

解决乱码问题

    1. 设置响应头 (优先级更高)
    1. 设置 meta 标签

完善资源请求错误处理

GET 和 POST 请求场景

image.png

请求区别

image.png

Node 模块化

将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程 叫做 模块化

好处

  • 防止命名冲突
  • 高复用性
  • 高维护性

模块暴露数据

image.png

注意

// require 返回的结果是 module.exports 的值

Nodejs 导入模块

// require

注意

image.png

导入文件夹的情况

如果导入的路径是一个文件夹,会首先检测该文件夹下的package.jsonmain 的属性指向的文件

如果 main 属性不存在,或 package.json 不存在,则会检测文件夹 index.js 和 index.json

如果还是没有,就报错
文件结构
  • pack
    • module
      • app.js
      • package.json
    • main.js
// package.json
{
    "main": "./app.js"
}
// app.js
module.exports = '我是一个模块'
// main.js
// 导入
const m = require('./module')
console.log(m) // 我是一个模块

require 导入模块基本流程

image.png

require 伪代码

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;
}
注意
const m = require('./m.js')
const m1 = require('./m.js')
// 结果只输出一次,因为有缓存

CommonJS 规范

Nodejs 是实现了 CommonJS 模块化规范。

npm 初始化包

    npm init

    每一个包都要有 package.json 文件
  
    包 的配置文件

image.png

npm 搜索包

image.png

开发依赖 和 生产依赖

image.png

{
  "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 全局安装

image.png

修改 windows 执行策略

  • image.png
  • 选 A

环境变量 Path

npm 安装指定版本包

// 格式
npm i <包名@版本号>

// 例子
npm i jquery@1.11.2

npm 配置命令别名

image.png

nvm 介绍 使用

用于管理 node 的版本管理工具,方便切换版本 image.png nvm 安装、卸载与使用(详细步骤)

express 框架

express 路由

路由确定了应用程序如何响应客户端对特定端点的请求

  • 一个路由的组成有 请求方法, 路径回调函数 组成

路由的使用

// 语法
    app.<method>(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')
})

获取请求报文参数

// 获取请求的路由规则
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('请求报文参数获取')
})

获取路由参数

// 获取请求的路由规则
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
// 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"
        }
    ]
}

响应设置

image.png

express 中间件

1. 什么是中间件
  
    本质:  回调函数
    中间件函数 可以像路由回调一样访问 请求对象(request) 响应对象(response)

2. 中间件的作用

    中间件的作用 就是 使用函数封装公共操作,简化代码

3. 中间件类型

    - 全局中间件
    - 路由中间件

定义全局中间件

/**
 * 全局中间件
 *  记录每个请求的 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')
})

路由中间件实践

/**
 * 路由中间件
 *  针对 /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
// 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')
})
注意点

image.png

获取请求体数据

image.png

/**
 * 搭建 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')
})
// login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>login</title>
</head>
<body>
    <div>
        <h1>login</h1>
        <form action="/login" method="post">
            <span>
                <p>username</p><input name="username" type="text">
            </span>
            <span>
                <p>password</p><input name="password" type="password">
            </span>
            <span>
                <button>login</button>
            </span>
        </form>
    </div>
</body>
</html>

防盗链

  防止外部网站盗用网站资源

防盗链实践

/**
 * 图片防盗链
 */
// 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('<h1>404</h1>')
            return
        }
    }
    next()
})

// 静态资源中间件设置
app.use(express.static(__dirname + '/public'))

// 4. 监听端口,启动服务
app.listen(3000, ()=>{
    console.log('listening on port 3000')
})

路由模块化

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

模板引擎是分离 用户界面 和 业务数据 的一种数据

下载

    npm i ejs --save

ejs 初体验

// 导入 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)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>
        我爱你 <%= china %>
    </h1>
</body>
</html>

ejs 列表渲染

// 导入 ejs
const ejs = require('ejs');

let xiyou = ['孙悟空','唐僧','猪八戒','沙悟净']

// 使用 ejs 渲染
let result = ejs.render(
    `<ul>
        <% xiyou.forEach(item => { %>
            <li><%= item %></li>
        <% }) %>
    </ul>`
    , {xiyou: xiyou})

console.log(result)

ejs 条件渲染

// 导入 ejs
const ejs = require('ejs');

let isLogin = false;

// 使用 ejs 渲染
let result = ejs.render(`
        <% if(isLogin){ %>
            <span>True</span>
        <% } else { %>
            <span>False</span>
        <% } %>`,
        {isLogin: isLogin})
console.log(result)

express 中使用ejs

// 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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= title %></title>
</head>
<body>
    <h2>测试</h2>
</body>
</html>

express-generator (生成器)

  通过应用生成器工具快速创建一个应用的骨架
    npm i -g express-generator

文件上传报文

<!-- enctype 文件上传必须属性 -->
<form action="/portrait" method="post"
    enctype="multipart/form-data">
    用户名: <input type="text" name="username"><br>
    头像: <input type="file" name="portrait"><br>
    <hr>
    <button>提交</button>
</form>

formidable (处理文件请求)

接口的介绍

前后端通讯的桥梁

RESTful API

image.png

json-server 工具

image.png

会话控制

image.png

Cookie

 cookie 是 HTTP 服务器发送到用户浏览器并保存在本地的一小块数据
  • cookie 是保存在浏览器端的一小块数据
  • cookie 是按照域名划分保存的
特点
浏览器向服务器发送请求时,会自动将 当前域名下 可用的 cookie 设置在请求头中,然后传递给服务器
浏览器操作 cookie
  1. 禁用 cookie
  2. 删除 cookie
  3. 查看 cookie
express 设置 Cookie
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. 运行流程

image.png

Session 中间件
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 的区别

image.png

CSRF 跨站请求伪造

Token

image.png

JWT

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,可用于基于 token 的身份认证
实例
// 导入 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);
    }
})
  • 校验成功 (解析数据)

image.png

  • 校验失败 (超时)

image.png

前后端开发扩展介绍

image.png