2-node 项目
svg-captcha 图形验证码
svg-captcha 是一个用于生成 SVG 格式验证码的插件库。它可以生成带有随机字符的 SVG 图片,常用于用户注册、登录等需要防止机器人自动提交的场景
- 生成随机字符的 SVG 图片验证码
- 支持自定义验证码长度、字符集、干扰线条数量、颜色和背景色等
- 可以生成数学表达式验证码
pnpm install svg-captcha在 Koa 应用中引入并使用 svg-captcha 生成验证码:
const Koa = require('koa');
const Router = require('koa-router');
const svgCaptcha = require('svg-captcha');
const app = new Koa();
const router = new Router();
router.get('/captcha', (ctx) => {
const captcha = svgCaptcha.create({
size: 6, // 验证码长度
ignoreChars: '0o1i', // 忽略的字符
noise: 2, // 干扰线条的数量
color: true, // 是否使用颜色
background: '#cc9966' // 背景颜色
});
ctx.type = 'image/svg+xml';
ctx.body = captcha.data;
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});配置选项
可以在生成验证码时传递配置选项,例如:
const captcha = svgCaptcha.create({
size: 6, // 验证码长度
ignoreChars: '0o1i', // 忽略的字符
noise: 2, // 干扰线条的数量
color: true, // 是否使用颜色
background: '#cc9966' // 背景颜色
});size: 验证码长度,默认为4ignoreChars: 忽略的字符,这些字符不会出现在验证码中noise: 干扰线条的数量,默认为1color: 是否使用颜色,默认为falsebackground: 背景颜色,默认为undefined
数学表达式验证码
还可以生成数学表达式验证码:
const captcha = svgCaptcha.createMathExpr({
mathMin: 1,
mathMax: 9,
mathOperator: '+', // 可选值:'+'、'-'
noise: 2,
color: true,
background: '#cc9966'
});mathMin: 数学表达式中最小的数字,默认为1mathMax: 数学表达式中最大的数字,默认为9mathOperator: 数学运算符,默认为'+',可选值为'+'和'-'
require.context
require.context 是 Webpack 提供的一个特性,用于动态加载模块,但它在 Node.js 环境中不可用。要在 Node.js 环境中实现类似的功能,可以使用 fs 模块和 path 模块来遍历目录并加载模块
import combineRoutes from "koa-combine-routers"
// 加载目录中的Router中间件
const moduleFiles = require.context("./modules", true, /\.js$/)
// reduce 方法去拼接 koa-combine-router 所需的数据结构 Object[]
const modules = moduleFiles.keys().reduce((items, path) => {
const value = moduleFiles(path)
items.push(value.default)
return items
}, [])
export default combineRoutes(modules)方法一:使用 fs 和 path 替换
以下是一个替代方案,使用 fs 和 path 模块来动态加载 ./modules 目录中的所有 JavaScript 文件:
import combineRoutes from "koa-combine-routers";
import fs from "fs";
import path from "path";
// 获取模块目录的绝对路径
const modulesDir = path.resolve(__dirname, "./modules");
// 读取目录中的所有文件
const moduleFiles = fs.readdirSync(modulesDir).filter(file => file.endsWith(".js"));
// 加载所有模块
const modules = moduleFiles.map(file => {
const filePath = path.join(modulesDir, file);
const module = require(filePath);
return module.default || module;
});
export default combineRoutes(modules);方法二:修改启动脚本
需要配置 webpack 打包的方式启动项目
{
"scripts": {
"start-node": "npx nodemon --exec babel-node src/index.js",
"start": "cross-env NODE_ENV=development webpack --watch --progress --config config/webpack.config.dev.js",
},
}nodemailer 邮件服务
邮件服务注意
使用客户端授权码,QQ邮箱14天限制
公共邮箱限制了发送频次、数量、群发
邮件服务:阿里云/亚马逊SES/SendCloud
使用 nodemailer 来发送电子邮件是 Node.js 应用中的一个常见需求。以下是配置和使用 nodemailer 通过 QQ 邮箱发送邮件的详细步骤
pnpm install nodemailer使用 QQ 邮箱发送邮件前,你需要做一些配置:
-
开启 SMTP 服务:登录 QQ 邮箱,进入“设置”->“账户”,开启“SMTP服务”。
-
获取授权码:开启 SMTP 服务后,QQ 邮箱会提供一个授权码,你需要使用这个授权码而不是你的 QQ 邮箱密码来进行 SMTP 身份验证
nodemailer
通过 nodemailer 使用 QQ 邮箱发送邮件的示例代码:
const nodemailer = require('nodemailer');
// 创建一个 SMTP 传输对象
const transporter = nodemailer.createTransport({
host: 'smtp.qq.com',
port: 465, // QQ 邮箱使用 465 端口进行 SSL 连接
secure: true, // 使用 SSL
auth: {
user: 'your-email@qq.com', // 你的 QQ 邮箱地址
pass: 'your-smtp-auth-code' // 你的 SMTP 授权码
}
});
// 设置邮件选项
const mailOptions = {
from: 'your-email@qq.com', // 发件人地址
to: 'recipient-email@example.com', // 收件人地址
subject: '测试邮件', // 邮件主题
text: '这是一封来自 Node.js 的测试邮件', // 邮件正文(文本格式)
html: '<p>这是一封来自 <b>Node.js</b> 的测试邮件</p>' // 邮件正文(HTML 格式)
};
// 发送邮件
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件发送成功: %s', info.messageId);
});保存上述代码为 sendEmail.js,然后在终端中运行:
node sendEmail.js如果配置正确,你应该会在终端中看到邮件发送成功的消息,并且收件人会收到你发送的邮件
dayjs
pnpm i dayjsqs
pnpm install qs uuid
pnpm i uuidkoa-jwt
https://jwt.io/ 在线查看校验 token
在 Node.js 中集成 koa-jwt 处理基于 JWT 身份验证。下面是一个完整的示例,展示在 Koa 中使用 koa-jwt
pnpm install koa-jwt jsonwebtoken编写一个简单的 Koa 应用,集成 koa-jwt:
const Koa = require('koa');
const Router = require('koa-router');
const jwt = require('jsonwebtoken');
const koaJwt = require('koa-jwt');
const app = new Koa();
const router = new Router();
const secret = 'your_secret_key'; // 用于签署 JWT 的密钥
// 用户登录的路由,成功登录后返回 JWT
router.post('/login', async (ctx) => {
const { username, password } = ctx.request.body;
// 在这里你应该验证用户名和密码
if (username === 'admin' && password === 'admin') {
const token = jwt.sign({ username }, secret, { expiresIn: '1h' });
ctx.body = { token };
} else {
ctx.status = 401;
ctx.body = { error: 'Invalid login' };
}
});
// 使用 koa-jwt 中间件保护的路由
router.get('/protected', koaJwt({ secret }), async (ctx) => {
ctx.body = { message: 'This is a protected route', user: ctx.state.user };
});
app
.use(require('koa-bodyparser')()) // 用于解析 POST 请求的 body
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});受保护的路由:
- 定义一个受保护的路由
/protected - 使用
koa-jwt中间件保护该路由。中间件会验证请求中的 JWT - 如果 JWT 验证通过,返回受保护的信息
启动服务器:
node app.js使用 curl 或 Postman 进行测试:
登录获取 Token:如果成功会收到一个包含 JWT 的响应
curl -X POST http://localhost:3000/login -d '{"username":"admin","password":"admin"}' -H "Content-Type: application/json"访问受保护的路由:
curl -H "Authorization: Bearer <your_jwt_token>" http://localhost:3000/protected替换 <your_jwt_token> 为登录时获取的 JWT。如果 token 有效,你会看到受保护的消息
jsonwebtoken
jsonwebtoken 是一个用于生成和验证 JSON Web Tokens (JWT) 的插件
- 生成 JWT:使用
jwt.sign方法生成一个新的 JWT - 验证 JWT:使用
jwt.verify方法验证一个 JWT 的有效性 - 解码 JWT:使用
jwt.decode方法解码一个 JWT,而不验证其签名
pnpm install jsonwebtoken生成 JWT
const jwt = require('jsonwebtoken');
// 定义一个密钥,用于签名 JWT
const secretKey = 'your-secret-key';
// 定义一个有效载荷
const payload = {
userId: 123,
username: 'john_doe'
};
// 生成 JWT
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
console.log('Generated Token:', token);验证 JWT
const jwt = require('jsonwebtoken');
// 定义一个密钥,用于验证 JWT
const secretKey = 'your-secret-key';
// 从请求中获取 JWT(例如,从 HTTP 头部)
const token = 'your-jwt-token';
try {
// 验证 JWT
const decoded = jwt.verify(token, secretKey);
console.log('Decoded Payload:', decoded);
} catch (err) {
console.error('Invalid Token:', err.message);
}配置选项
jwt.sign 方法
payload:JWT 的有效载荷,可以是对象、字符串或 BuffersecretOrPrivateKey:用于签名 JWT 的密钥或私钥options:可选的配置对象,例如:expiresIn:设置 JWT 的过期时间,例如'1h'表示 1 小时algorithm:指定用于签名的算法,默认是'HS256'
jwt.verify 方法
token:要验证的 JWT。secretOrPublicKey:用于验证 JWT 的密钥或公钥options:可选的配置对象,例如:algorithms:指定允许的算法列表
npm-run-all
npm-run-all 是一个用于并行或串行运行多个 npm 脚本的插件库。简化复杂的 npm 脚本执行流程,特别是在需要同时运行多个任务时非常有用
-s或--serial:串行运行脚本(默认行为)-p或--parallel:并行运行脚本。--continue-on-error:即使某个脚本失败,也继续运行后续脚本--race:一旦有一个脚本成功或失败,立即终止其他脚本
pnpm install npm-run-all -D串行运行脚本
使用 run-s 命令可以串行运行多个脚本,npm-run-all clean build 将会先运行 clean 脚本,然后运行 build 脚本
{
"scripts": {
"clean": "rimraf dist",
"build": "cross-env NODE_ENV=production webpack --config config/webpack.config.prod.js",
"start": "npm-run-all clean build"
}
}并行运行脚本
使用 run-p 命令可以并行运行多个脚本,npm-run-all -p server watch 将会并行运行 server 和 watch 脚本
{
"scripts": {
"server": "npx nodemon --exec babel-node src/index.js",
"watch": "cross-env NODE_ENV=development webpack --watch --progress --config config/webpack.config.dev.js",
"start": "npm-run-all -p server watch"
}
}bcrypt
bcrypt 是一个用于加密和验证密码的插件库。它使用了 Blowfish 加密算法,提供了强大的安全性,常用于存储用户密码
- 加密密码:使用
bcrypt.hash方法加密密码 - 验证密码:使用
bcrypt.compare方法验证密码是否匹配已加密的密码 - 生成盐:使用
bcrypt.genSalt方法生成盐
pnpm install bcrypt在 Node.js 应用中使用 bcrypt 进行密码加密和验证的示例:
加密密码
const bcrypt = require('bcrypt');
// 定义一个密码
const password = 'myPlainPassword';
// 定义盐的轮数(越高越安全,但计算时间越长)
const saltRounds = 10;
// 加密密码
bcrypt.hash(password, saltRounds, (err, hash) => {
if (err) {
console.error('Error hashing password:', err);
} else {
console.log('Hashed Password:', hash);
}
});验证密码
const bcrypt = require('bcrypt');
// 假设这是从数据库中获取的已加密密码
const hashedPassword = '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36Zf4a2B5N9yK3e6k9b5QK6';
// 用户输入的密码
const password = 'myPlainPassword';
// 验证密码
bcrypt.compare(password, hashedPassword, (err, result) => {
if (err) {
console.error('Error comparing password:', err);
} else if (result) {
console.log('Password is valid!');
} else {
console.log('Password is invalid!');
}
});bcrypt.compare 方法之所以只需要传入原始数据(明文密码)和加密后的数据(哈希密码)就可以进行对比,是因为 bcrypt 在生成哈希时会将盐(salt)嵌入到哈希中,并且使用了相同的算法进行加密
-
当使用
bcrypt.hash方法加密密码时,bcrypt会生成一个随机的盐,并将这个盐与密码一起进行哈希运算。生成的哈希值包含了盐和加密后的密码javascriptconst saltRounds = 10; const hashedPassword = await bcrypt.hash(password, saltRounds); -
当使用
bcrypt.compare方法进行密码验证时,bcrypt会从存储的哈希值中提取出盐,并使用这个盐对输入的明文密码进行相同的哈希运算。然后会将生成的哈希值与存储的哈希值进行比较javascriptconst isValid = await bcrypt.compare(password, hashedPassword);
VSCode 配置 alias 的支持
VSCode 插件 Node modules resolve
生成 jsconfig.json
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": false,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}可能需要重启 编辑器