前言

Koa 是一个由 Express 的原始开发者创建的 Node.js 后端框架,旨在提供更小、更灵活的基础后端框架。

koa - npm (npmjs.com)

安装准备

安装nodemon

1
npm i nodemon -g

安装koa

1
npm i koa

在package.json文件中使用es6规范且将调试改为使用nodemon

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "kora",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"koa": "^2.15.3"
}
}

创建路由

首先需要创建一个koa app 实例,使用ctx上下文对象来获取request,使用ctx.body来返回具体相应的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Koa from "koa";

const hostname = "127.0.0.1";
const port = "8008"

const app = new Koa();

app.use(async (ctx)=>{
ctx.body = "hello world"
})


app.listen(port,hostname,()=>{
console.log(`Server running at http://${hostname}:${port}/`);
})

koa app实例还可以对其他非法路由进行捕获处理

1
2
3
4
5
6
app.use(async (ctx) => {
if (!ctx.body) {
ctx.status = 404;
ctx.body = "404 Not Found";
}
});

ctx中具有request和response两个对象的性质,和nodejs中的使用方法相同,可以使用ctx.requestctx.response获取特定的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//尝试访问 http://127.0.0.1:8008/index?id=1&name=koa,可以看到页面显示id: 1, name: koa。
router.get("/index", async (ctx) => {
if (ctx.request.method == "GET") {
let id = ctx.query.id;
let name = ctx.query.name;
ctx.status = 200;
ctx.method = "GET";
ctx.type = "Application/json";
ctx.path = "/index";
ctx.body = {
id: id,
name: name,
};
}
});

在网页中便可以看到显示格式为Json(JavaScript Object Notation)

1
2
3
4
{
"id": "1",
"name": "koa"
}

洋葱模型

app.use()方法用于注册中间件,一个use() 就是一个中间件,可以使用next()进行中间件的阻断。

当程序遇到next()的时候,会暂停当前中间件的执行,将控制权传递给下一个中间件。控制权的来回切换符合洋葱模型,看如下的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Koa from "koa";

const hostname = "127.0.0.1"; //服务器监听的ip地址
const port = 8008; //服务器监听的端口号

const app = new Koa();

// 下面是洋葱模型的一个实现例子
app.use(async (ctx, next) => {
console.log("1");
await next();
console.log("2");
});

app.use(async (ctx, next) => {
console.log("3");
await next();
console.log("4");
});

app.use(async (ctx) => {
ctx.body = "Hello World";
});

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

打印得到的结果为:

1
2
3
4
1
3
4
2

如果出现两次打印结果,这是因为浏览器默认会申请访问网站的ico(图标),可以在调试工具中禁用

安装与配置路由

安装@koa/router

1
npm i @koa/router

koa-router 兼容 koa1 和 koa2 的历史版本
@koa/router 专为 koa2 设计的新版本

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "kora",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@koa/router": "^12.0.1",
"koa": "^2.15.3"
}
}

创建Router实例并将其运用到Koa app实例中,下面是一个简单的例子,访问默认的网址会显示Hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Koa from "koa";
import Router from "@koa/router";

const hostname = "127.0.0.1"; //服务器监听的ip地址
const port = 8008; //服务器监听的端口号

const app = new Koa();
const router = new Router();


router.get("/", async (ctx) => {
ctx.body = "Hello World!";
});

app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

对于带有参数的访问,可以使用query和params对象进行获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//尝试访问 http://127.0.0.1:8008/index?id=1&name=koa,可以看到页面显示id: 1, name: koa。
router.get("/index", async (ctx) => {
let id = ctx.query.id;
let name = ctx.query.name;
ctx.status = 200;
ctx.body = `id: ${id}, name: ${name}`;
});

//尝试访问 http://127.0.0.1:8008/user/id/1/name/koa,可以看到页面显示id: 1, name: koa。
router.get("/user/id/:id/name/:name", async (ctx) => {
let id = ctx.params.id;
let name = ctx.params.name;
ctx.status = 200;
ctx.body = `id: ${id}, name: ${name}`;
});


app.use(router.routes());

在创建路由的时候,可以使用prefix参数进行路由的分组

1
2
3
4
5
6
7
8
9
10
11
12
13
const router2 = new Router({prefix: "/api"});

// 访问 http://127.0.0.1:8008/api/get,可以看到页面显示get。
router2.get("/get", async (ctx) => {
ctx.body = "get";
});

// 访问 http://127.0.0.1:8008/api/post,可以看到页面显示post。
router2.get("/post", async (ctx) => {
ctx.body = "post";
});

app.use(router2.routers())

还可以使用Router对象的redirect方法进行网页重定向

1
2
3
4
// 访问 http://127.0.0.1:8008/redirect,页面会自动跳转到b站首页。
router.redirect("/redirect", "https://www.bilibili.com/");

app.use(router.routers())

post请求

安装@koa/post

1
npm i @koa/bodyparser

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "kora",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@koa/bodyparser": "^5.1.1",
"@koa/router": "^12.0.1",
"koa": "^2.15.3"
}
}

让中间件使用bodyparser,要获取请求体中的参数,使用ctx.request.body.属性名来获取参数数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import koaBody from "@koa/bodyparser";

const app = new Koa();
const router = new Router();

app.use(koaBody());

// 尝试访问 http://127.0.0.1:8008/postUrl
router.post("/postUrl", async (ctx) => {
let id = ctx.request.body.id;
let name = ctx.request.body.name;

ctx.status = 200;
ctx.body = `id: ${id}, name: ${name}`;
});

// 尝试访问 http://127.0.0.1:8008/postJson
router.post("/postJSon", async (ctx) => {
let id = ctx.request.body.id;
let name = ctx.request.body.name;

ctx.status = 200;
ctx.body = `id:${id}, name:${name}`;
});

// 404处理
app.use(async (ctx) => {
if (!ctx.body) {
ctx.status = 404;
ctx.body = "404 Not Found";
}
});

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

使用apiPost工具进行模拟请求

alt text alt text

完整代码备份如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import Koa from "koa";
import Router from "@koa/router";
import koaBody from "@koa/bodyparser";

const hostname = "127.0.0.1"; //服务器监听的ip地址
const port = 8008; //服务器监听的端口号

const app = new Koa();
const router = new Router();

app.use(koaBody());

// 尝试访问 http://127.0.0.1:8008/postUrl
router.post("/postUrl", async (ctx) => {
let id = ctx.request.body.id;
let name = ctx.request.body.name;

ctx.status = 200;
ctx.body = `id: ${id}, name: ${name}`;
});

// 尝试访问 http://127.0.0.1:8008/postJson
router.post("/postJSon", async (ctx) => {
let id = ctx.request.body.id;
let name = ctx.request.body.name;

ctx.status = 200;
ctx.body = `id:${id}, name:${name}`;
});

router.get("/", async (ctx) => {
ctx.body = "Hello World!";
});

//尝试访问 http://127.0.0.1:8008/index?id=1&name=koa,可以看到页面显示id: 1, name: koa。
router.get("/index", async (ctx) => {
if (ctx.request.method == "GET") {
let id = ctx.query.id;
let name = ctx.query.name;
ctx.status = 200;
ctx.method = "GET";
ctx.type = "Application/json";
ctx.path = "/index";
// ctx.body = `id: ${id}, name: ${name}`;
ctx.body = {
id: id,
name: name,
};
}
});

//尝试访问 http://127.0.0.1:8008/user/id/1/name/koa,可以看到页面显示id: 1, name: koa。
router.get("/user/id/:id/name/:name", async (ctx) => {
let id = ctx.params.id;
let name = ctx.params.name;
ctx.status = 200;
ctx.body = `id: ${id}, name: ${name}`;
});

const router2 = new Router({ prefix: "/api" });

// 访问 http://127.0.0.1:8008/api/get,可以看到页面显示get。
router2.get("/get", async (ctx) => {
ctx.body = "get";
ctx.response.status = 200;
});

// 访问 http://127.0.0.1:8008/api/post,可以看到页面显示post。
router2.get("/post", async (ctx) => {
ctx.body = "post";
});

// 访问 http://127.0.0.1:8008/redirect,页面会自动跳转到b站首页。
router.redirect("/redirect", "https://www.bilibili.com/");

app.use(router.routes());
app.use(router2.routes());

app.use(async (ctx) => {
if (!ctx.body) {
ctx.status = 404;
ctx.body = "404 Not Found";
}
});

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

注意,koaBody中有一个关键的parsedMethods参数,如果不额外设置,默认为['POST', 'PUT', 'PATCH'],使用范围以外的请求方法会无法获取body(如DELETE请求),其他参数详见@koa/bodyparser - npm (npmjs.com)

错误捕获

使用try catch语句进行错误的捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import Koa from "koa";
import Router from "@koa/router";

const hostname = "127.0.0.1"; //服务器监听的ip地址
const port = 8008; //服务器监听的端口号

const app = new Koa();
const router = new Router();

// 直接抛出一个错误,网页网络会直接显示500错误
router.get("/", async (ctx) => {
throw new Error("error");
});

// 请求先通过中间件,如果中间件没有捕获到错误,就会进入到路由处理函数,
// 如果路由处理函数抛出了错误,就会被中间件捕获到,然后返回给客户端。
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = 500;
ctx.body = "网络错误";
}
});

// 进入路由处理函数
app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

允许跨域请求

安装

1
npm i @koa/cors

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "kora",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@koa/bodyparser": "^5.1.1",
"@koa/cors": "^5.0.0",
"@koa/router": "^12.0.1",
"koa": "^2.15.3"
}
}

如果出现跨域请求会出现报错:

Access to fetch at ‘http://127.0.0.1:8008/‘ from origin ‘null’ has been blocked by CORS policy:
No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
If an opaque response serves your needs,
set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

什么情况下算跨域请求呢?

在web开发中,”域” 主要指的是网络请求的来源(origin),由协议(如:http或https)、域名和端口三部分组成,任何一部分的不同都算跨域

想要允许跨域请求,让app对象使用Cors即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Koa from "koa";
import Router from "@koa/router";
import Cors from "@koa/cors";

const hostname = "127.0.0.1"; //服务器监听的ip地址
const port = 8008; //服务器监听的端口号

const app = new Koa();
const router = new Router();

app.use(Cors());

router.get("/", async (ctx) => {
ctx.status = 200;
ctx.body = "Hello World";
});

// 进入路由处理函数
app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

上传文件

安装

1
npm i @koa/multer

首先创建一个磁盘存储引擎,并规定上传的路径(服务端的文件夹)和文件名规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Multer from "@koa/multer";
import path from "path"

// 定义一个磁盘存储引擎
const storage = Multer.diskStorage({
destination: (request, file, callbackFunc) => {
//指定文件保存路径为upload文件夹
callbackFunc(null, "./upload");
},
filename: (request, file, callbackFunc) => {
//设置文件名 为上传时间+文件原始后缀名
callbackFunc(null, Date.now() + path.extname(file.originalname));
},
});

接着定一个multer对象,可以限定文件的上传类型和文件大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义一个 Multer 对象
const multer = Multer({
storage, //磁盘存储引擎
limits: {
//限制条件
fileSize: 2 * 1024 * 1024, // 限制文件大小为2MB
// 限制文件数量
// files: 1,
},
fileFilter: (request, file, callbackFunc) => {
//文件过滤器
let allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
if (allowedTypes.includes(file.mimetype)) {
callbackFunc(null, true);
} else {
callbackFunc(new Error("不支持的文件类型"), false);
}
},
});

创建后,在对应路由中使用multer对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Koa from "koa";
import Router from "@koa/router";
import Cors from "@koa/cors";

const app = Koa();
app.use(Cors());

const router = Router();

// 访问网址为:http://127.0.0.1:3000/upload
router.post("/upload", multer.single("file"), async (ctx) => {
const file = ctx.request.file;
if (file) {
ctx.body = {
status: "success",
message: "文件上传成功",
data: file,
};
}else {
ctx.body = {
status: "error",
message: "文件上传失败",
};
}
});

app.use(router.routers())


app.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});

选择txt进行上传,打印不支持文件类型

alt text

选择大于2MB文件进行上传,返回文件过大(file too large)

alt text

以下为成功的例子

alt text

上传的文件都会收集到服务端的upload文件夹中

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import Koa from "koa";
import Router from "@koa/router";
import Cors from "@koa/cors";
import BodyParser from "@koa/bodyparser";
import Multer from "@koa/multer";
import path from "path";

const host = "127.0.0.1";
const port = 3000;

const app = new Koa();

const router = new Router();

// 允许跨域
app.use(Cors());
// 解析请求体
app.use(BodyParser());

// 定义一个磁盘存储引擎
const storage = Multer.diskStorage({
destination: (request, file, callbackFunc) => {
//指定文件保存路径为upload文件夹
callbackFunc(null, "./upload");
},
filename: (request, file, callbackFunc) => {
//设置文件名 为上传时间+文件原始后缀名
callbackFunc(null, Date.now() + path.extname(file.originalname));
},
});

// 定义一个 Multer 对象
const multer = Multer({
storage, //磁盘存储引擎
limits: {
//限制条件
fileSize: 2 * 1024 * 1024, // 限制文件大小为2MB
// 限制文件数量
// files: 1,
},
fileFilter: (request, file, callbackFunc) => {
//文件过滤器
let allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
if (allowedTypes.includes(file.mimetype)) {
callbackFunc(null, true);
} else {
callbackFunc(new Error("不支持的文件类型"), false);
}
},
});

// 访问网址为:http://127.0.0.1:3000/upload
router.post("/upload", multer.single("file"), async (ctx) => {
const file = ctx.request.file;
if (file) {
ctx.body = {
status: "success",
message: "文件上传成功",
data: file,
};
}else {
ctx.body = {
status: "error",
message: "文件上传失败",
};
}
});

app.use(async (ctx, next) => {
//错误处理中间件
try {
await next();
} catch (err) {
//console.log('err:', err)
ctx.status = 500;
ctx.body = "err: " + err.message;
}
});

app.use(router.routes()); //路由处理中间件

app.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});

cookie通常用来存储用户当前的状态信息

在koa中通过ctx.cookies.set()来设置cookie信息,常用maxAgehttpOnly参数来分别设置cookie的保存时间和是否允许脚本获取cookie

如果设置的不是英文,而是其他字符,常见的是通过uri来进行加密和解密

后续可以通过ctx.cookies.get方法获取相应的cookie信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Koa from "koa";
import Router from "@koa/router";

const hostname = "127.0.0.1";
const port = 8008;

const app = new Koa();
const router = new Router(); //实例化一个 Router 对象

//cookie可用于在浏览器中存储数据
router.get("/", async (ctx) => {
//赋值
ctx.cookies.set("name", encodeURIComponent("百度")); //encodeURIComponent:uri编码
ctx.cookies.set("web", "baidu.com", {
//30秒 [maxAge:有效期 单位:毫秒]
maxAge: 30 * 1000,
//httpOnly默认为true 可以防止跨站脚本攻击(XSS)、减少跨站请求伪造(CSRF)
httpOnly: false, //false为允许浏览器通过js访问和修改该cookie
});

//取值 - 在同一个请求内, 无法立即获取到刚刚设置的cookie的值
let name = ctx.cookies.get("name");
console.log("name:", decodeURIComponent(name)); //decodeURIComponent:uri解码

ctx.body = "欢迎访问百度";
});

app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`);
});

可以在调试工具中看到cookie信息

alt text

如果要删除cookie,可以通过设置空白值的方法进行清除

1
2
3
4
5
6
router.get("/", async (ctx) => {
// 其他代码

// 删除cookie
ctx.cookies.set("name","",{maxAge: 0})
})

Session

session用于服务端存储状态信息,通过 Session ID 在 Cookie 中的存在,来识别和跟踪用户,同时在服务器端安全地存储用户的状态和数据。

和cookie不同的是,除了设置属性值和过期时间,session需要单独Session ID(存储在cookie中的唯一标识)和密钥(防止数据被篡改)

安装

1
npm i koa-session

需要注意的是,此方法会将Session存储到客户端的cookie中
编码部分采用的是base64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import Koa from "koa";
import Router from "@koa/router";
import Session from "koa-session";

const hostname = "127.0.0.1";
const port = 8008;

const app = new Koa();
const router = new Router();

//koa-session 默认将 session 数据存储在客户端的 cookie 中
app.keys = ["session.koa"]; //设置会话签名的密钥
const CONFIG = {
key: "koa", //存储在 cookie 中的键名
maxAge: 24 * 60 * 60 * 1000, //24小时 有效期(单位:毫秒)
signed: true, //是否启用会话签名, 用于防止CSRF攻击
secure: false, //是否仅通过 https 协议发送 cookie
};
app.use(Session(CONFIG, app));

router.get("/", async (ctx) => {
//赋值
ctx.session.name = "百度";
ctx.session.url = "baidu.com";
if (!ctx.session.user) {
ctx.session.user = 1;
} else {
ctx.session.user += 1;
}

//取值
let name = ctx.session.name;
console.log("name:", name);

ctx.body = "用户:" + ctx.session.user;
});

app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`);
});

在浏览器的调试工具中可以查看

alt text

通过以下方法可以删除session

1
2
3
4
5
6
router.get("/", async (ctx) => {
// 其他代码

ctx.session = null //清空所有session信息
delete ctx.session.name //只删除 session 中的 name 属性
});

jwt

JWT(JSON Web Token)是一种用于安全传输信息的开放标准(RFC 7519),通常用于身份验证和信息交换。

JWT 由三部分组成:

  • 头部(Header):通常包含令牌的类型(JWT)和所使用的签名算法(如 HMAC SHA256)。
  • 载荷(Payload):包含声明(claims),即要传输的数据,可以是用户信息、权限等。
  • 签名(Signature):通过头部和载荷生成的签名,用于验证令牌的完整性和来源。

格式为:header.payload.signature

安装依赖

1
npm i jsonwebtoken

需要定义一个生成token的函数,需要传入一个key作为加密的密匙。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import JWT from 'jsonwebtoken'

//生成token函数
let generateToken = (key) => {
let id = 1; //用户唯一id
let now = Math.floor(Date.now() / 1000); //当前时间戳 单位:秒
let expire = 24 * 60 * 60; //24小时

//负载
let payload = {
sub: id, //Subject 主题 (用户唯一id)
iss: "baidu.com", //Issuer 发行者
iat: now, //Issued At 发行时间
nbf: now, //Not Before 生效时间
exp: now + expire, //Expiration Time 过期时间
aud: [""], //Audience 观众字段为空,表示没有观众限制,可以被任何接收方处理
data: {
//自定义数据
name: "gcnanmu",
gender: "man",
},
};

//使用负载(payload)、密钥(key)和指定的签名算法(HS256)生成token
let token = JWT.sign(payload, key, { algorithm: "HS256" });
return token;
};

解密可以使用如下代码:

1
2
3
4
let parseToken = (token, key) => {
let payload = JWT.verify(token, key);
return JSON.stringify(payload);
};

如果想要解析其中的信息,可以使用:https://jwt.io/ 这个网站

其中得到的结果为:

1
2
生成token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImlzcyI6ImJhaWR1LmNvbSIsImlhdCI6MTcyMzEyOTc5NiwibmJmIjoxNzIzMTI5Nzk2LCJleHAiOjE3MjMyMTYxOTYsImF1ZCI6WyIiXSwiZGF0YSI6eyJuYW1lIjoiZ2NuYW5tdSIsImdlbmRlciI6Im1hbiJ9fQ.9XLXbBDitorJG22p7aZ64OArcTI0EM2gJgtsHVQOEUE
解析token: {"sub":1,"iss":"baidu.com","iat":1723129796,"nbf":1723129796,"exp":1723216196,"aud":[""],"data":{"name":"gcnanmu","gender":"man"}}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import Koa from "koa";
import Router from "@koa/router";
import JWT from "jsonwebtoken";

const hostname = "127.0.0.1";
const port = 8008;

const app = new Koa();
const router = new Router();

router.get("/", (ctx) => {
const key = "koa2";
const token = generateToken(key);
ctx.status = 200;
let result =
"生成token: " + token + "\n" + "解析token: " + parseToken(token, key);
ctx.body = result;
});

//生成token函数
let generateToken = (key) => {
let id = 1; //用户唯一id
let now = Math.floor(Date.now() / 1000); //当前时间戳 单位:秒
let expire = 24 * 60 * 60; //24小时

//负载
let payload = {
sub: id, //Subject 主题 (用户唯一id)
iss: "baidu.com", //Issuer 发行者
iat: now, //Issued At 发行时间
nbf: now, //Not Before 生效时间
exp: now + expire, //Expiration Time 过期时间
aud: [""], //Audience 观众字段为空,表示没有观众限制,可以被任何接收方处理
data: {
//自定义数据
name: "gcnanmu",
gender: "man",
},
};

//使用负载(payload)、密钥(key)和指定的签名算法(HS256)生成token
let token = JWT.sign(payload, key, { algorithm: "HS256" });
return token;
};

// 解析token函数
let parseToken = (token, key) => {
let payload = JWT.verify(token, key);
return JSON.stringify(payload);
};

app.use(router.routes());

app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`);
});