From 43d2f4a765152b2a10a52e18bf363d3e874701bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BA=9A=E6=98=95?= <1549469775@qq.com> Date: Fri, 15 Aug 2025 14:12:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E6=B3=A8=E5=86=8C=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=AA=8C=E8=AF=81=E7=A0=81=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=E5=92=8C=E5=AE=89=E5=85=A8=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 `svg-captcha` 依赖包用于生成图形验证码 - 在 `PageController.js` 中实现验证码生成接口 `/captcha`,并添加5分钟过期时间控制 - 修改注册逻辑,增加验证码校验、随机数防重复提交等安全措施 - 优化注册页面模板,添加验证码输入框和隐藏的随机数字段 - 简化日志配置,移除部分未使用的日志记录器 - 修复示例任务的引号格式问题,保持代码风格统一 - 新增 Toast 中间件,提供统一的提示消息设置方法 - 优化错误处理中间件,将错误信息重定向到当前页面并显示 --- bun.lockb | Bin 137792 -> 138870 bytes package.json | 1 + src/controllers/Page/PageController.js | 62 +++++++++++++++++++++-- src/jobs/exampleJob.js | 12 ++--- src/logger.js | 88 ++++++++++++++++----------------- src/main.js | 10 ++-- src/middlewares/ResponseTime/index.js | 4 +- src/middlewares/Toast/index.js | 14 ++++++ src/middlewares/errorHandler/index.js | 5 +- src/middlewares/install.js | 2 + src/views/page/register/index.pug | 5 ++ 11 files changed, 141 insertions(+), 62 deletions(-) create mode 100644 src/middlewares/Toast/index.js diff --git a/bun.lockb b/bun.lockb index cc49194da6658b04221713b27c8bb7e176b844b7..f49cad5e0cdd51c63f463114f4516a2d477f8c68 100644 GIT binary patch delta 23190 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 B=fD5} delta 22641 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 diff --git a/package.json b/package.json index 2e71fbb..ad1e94c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "path-to-regexp": "^8.2.0", "pug": "^3.0.3", "sqlite3": "^5.1.7", + "svg-captcha": "^1.4.0", "vite-plugin-static-copy": "^3.1.0" }, "_moduleAliases": { diff --git a/src/controllers/Page/PageController.js b/src/controllers/Page/PageController.js index 28e41b5..99c00ad 100644 --- a/src/controllers/Page/PageController.js +++ b/src/controllers/Page/PageController.js @@ -1,6 +1,8 @@ import Router from "utils/router.js" import UserService from "services/UserService.js" import SiteConfigService from "services/SiteConfigService.js" +import svgCaptcha from "svg-captcha" +import CommonError from "@/utils/error/CommonError" class PageController { constructor() { @@ -33,6 +35,26 @@ class PageController { ctx.body = { success: true, message: "登录成功" } } + async captchaGet(ctx) { + var captcha = svgCaptcha.create({ + size: 4, // 个数 + width: 100, // 宽 + height: 30, // 高 + fontSize: 38, // 字体大小 + color: true, // 字体颜色是否多变 + noise: 2, // 干扰线几条 + }) + // 记录验证码信息(文本+过期时间) + // 这里设置5分钟后过期 + const expireTime = Date.now() + 5 * 60 * 1000 + ctx.session.captcha = { + text: captcha.text.toLowerCase(), // 转小写,忽略大小写验证 + expireTime: expireTime, + } + ctx.type = "image/svg+xml" + ctx.body = captcha.data + } + async registerGet(ctx) { if (ctx.state.user) { ctx.cookies.set("toast", JSON.stringify({ type: "error", message: encodeURIComponent("用户已登录") }), { @@ -42,15 +64,48 @@ class PageController { }) return ctx.redirect("/?msg=用户已登录") } - return await ctx.render("page/register/index", { site_title: "注册" }) + // TODO 多个 + ctx.session.registerRandomStr = Math.ceil(Math.random() * 100000000000000) + return await ctx.render("page/register/index", { site_title: "注册", randomStr: ctx.session.registerRandomStr }) } async registerPost(ctx) { - const { username, password } = ctx.request.body + const { username, password, code, randomStr } = ctx.request.body + + if (!ctx.session.registerRandomStr) { + throw new CommonError("缺少随机数") + } + if (ctx.session.registerRandomStr + "" !== randomStr + "") { + throw new CommonError("随机数不匹配") + } + delete ctx.session.registerRandomStr + + // 检查Session中是否存在验证码 + if (!ctx.session.captcha) { + throw new CommonError("验证码不存在,请重新获取") + } + + const { text, expireTime } = ctx.session.captcha + + // 检查是否过期 + if (Date.now() > expireTime) { + // 过期后清除Session中的验证码 + delete ctx.session.captcha + throw new CommonError("验证码已过期,请重新获取") + } + + if (!code) { + throw new CommonError("请输入验证码") + } + + if (code.toLowerCase() !== text) { + throw new CommonError("验证码错误") + } + + delete ctx.session.captcha // try { await this.userService.register({ username, password, role: "user" }) - // ctx.cookies.set("toast", JSON.stringify({ type: "success", message: "注册成功" }), { // maxAge: 1, // httpOnly: false, @@ -97,6 +152,7 @@ class PageController { router.get("/about", controller.pageGet("page/about/index"), { auth: false }) router.get("/login", controller.loginGet.bind(controller), { auth: "try" }) router.post("/login", controller.loginPost.bind(controller), { auth: false }) + router.get("/captcha", controller.captchaGet.bind(controller), { auth: false }) router.get("/register", controller.registerGet.bind(controller), { auth: "try" }) router.post("/register", controller.registerPost.bind(controller), { auth: false }) router.post("/logout", controller.logout.bind(controller), { auth: true }) diff --git a/src/jobs/exampleJob.js b/src/jobs/exampleJob.js index c79fc8b..4e0387c 100644 --- a/src/jobs/exampleJob.js +++ b/src/jobs/exampleJob.js @@ -1,11 +1,11 @@ -import { jobLogger } from "@/logger"; +import { jobLogger } from "@/logger" export default { - id: 'example', - cronTime: '*/10 * * * * *', // 每10秒执行一次 + id: "example", + cronTime: "*/10 * * * * *", // 每10秒执行一次 task: () => { - jobLogger.info('Example Job 执行了'); + jobLogger.info("Example Job 执行了") }, options: {}, - autoStart: false -}; + autoStart: false, +} diff --git a/src/logger.js b/src/logger.js index 2312d90..8e5285e 100644 --- a/src/logger.js +++ b/src/logger.js @@ -2,18 +2,18 @@ import log4js from "log4js" log4js.configure({ appenders: { - debug: { - type: "file", - filename: "logs/debug.log", - maxLogSize: 102400, - pattern: "-yyyy-MM-dd.log", - alwaysIncludePattern: true, - backups: 3, - layout: { - type: 'pattern', - pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', - }, - }, + // debug: { + // type: "file", + // filename: "logs/debug.log", + // maxLogSize: 102400, + // pattern: "-yyyy-MM-dd.log", + // alwaysIncludePattern: true, + // backups: 3, + // layout: { + // type: 'pattern', + // pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', + // }, + // }, all: { type: "file", filename: "logs/all.log", @@ -26,18 +26,18 @@ log4js.configure({ pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', }, }, - error: { - type: "file", - filename: "logs/error.log", - maxLogSize: 102400, - pattern: "-yyyy-MM-dd.log", - alwaysIncludePattern: true, - backups: 3, - layout: { - type: 'pattern', - pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', - }, - }, + // error: { + // type: "file", + // filename: "logs/error.log", + // maxLogSize: 102400, + // pattern: "-yyyy-MM-dd.log", + // alwaysIncludePattern: true, + // backups: 3, + // layout: { + // type: 'pattern', + // pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', + // }, + // }, jobs: { type: "file", filename: "logs/jobs.log", @@ -50,18 +50,18 @@ log4js.configure({ pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', }, }, - site: { - type: "file", - filename: "logs/site.log", - maxLogSize: 102400, - pattern: "-yyyy-MM-dd.log", - alwaysIncludePattern: true, - backups: 3, - layout: { - type: 'pattern', - pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', - }, - }, + // site: { + // type: "file", + // filename: "logs/site.log", + // maxLogSize: 102400, + // pattern: "-yyyy-MM-dd.log", + // alwaysIncludePattern: true, + // backups: 3, + // layout: { + // type: 'pattern', + // pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %m', + // }, + // }, console: { type: "console", layout: { @@ -72,18 +72,18 @@ log4js.configure({ }, categories: { jobs: { appenders: ["console", "jobs"], level: "ALL" }, - site: { appenders: ["site"], level: "ALL" }, - console: { appenders: ["console"], level: "ALL" }, - error: { appenders: ["console", "error"], level: "error" }, + // site: { appenders: ["site"], level: "ALL" }, + // console: { appenders: ["console"], level: "ALL" }, + // error: { appenders: ["console", "error"], level: "error" }, default: { appenders: ["console", "all"], level: "ALL" }, - debug: { appenders: ["debug"], level: "debug" }, + // debug: { appenders: ["debug"], level: "debug" }, }, }) // 导出常用logger实例,便于直接引用 export const logger = log4js.getLogger(); -export const debugLogger = log4js.getLogger('debug'); +// export const debugLogger = log4js.getLogger('debug'); export const jobLogger = log4js.getLogger('jobs'); -export const errorLogger = log4js.getLogger('error'); -export const siteLogger = log4js.getLogger('site'); -export const consoleLogger = log4js.getLogger('console'); +// export const errorLogger = log4js.getLogger('error'); +// export const siteLogger = log4js.getLogger('site'); +// export const consoleLogger = log4js.getLogger('console'); diff --git a/src/main.js b/src/main.js index 7a1d876..560618d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ // 日志、全局插件、定时任务等基础设施 -import { consoleLogger } from "./logger.js" +import { logger } from "./logger.js" import "./jobs/index.js" // 第三方依赖 @@ -31,10 +31,10 @@ const server = app.listen(PORT, () => { return "localhost" } const localIP = getLocalIP() - consoleLogger.trace(`===================【服务器地址】====================`) - consoleLogger.trace(` http://localhost:${port} (本地地址) `) - consoleLogger.trace(` http://${localIP}:${port} (本地地址) `) - consoleLogger.trace(`===================【服务器地址】====================`) + logger.trace(`===================【服务器地址】====================`) + logger.trace(` http://localhost:${port} (本地地址) `) + logger.trace(` http://${localIP}:${port} (本地地址) `) + logger.trace(`===================【服务器地址】====================`) }) export default app diff --git a/src/middlewares/ResponseTime/index.js b/src/middlewares/ResponseTime/index.js index 6712512..8312814 100644 --- a/src/middlewares/ResponseTime/index.js +++ b/src/middlewares/ResponseTime/index.js @@ -1,4 +1,4 @@ -import { siteLogger, logger } from "@/logger" +import { logger } from "@/logger" // 静态资源扩展名列表 const staticExts = [".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".map", ".woff", ".woff2", ".ttf", ".eot"] @@ -23,7 +23,7 @@ export default async (ctx, next) => { const ms = Date.now() - start ctx.set("X-Response-Time", `${ms}ms`) if (ms > 500) { - siteLogger.info(`${ctx.path} | ⏱️ ${ms}ms`) + logger.info(`${ctx.path} | ⏱️ ${ms}ms`) } return } diff --git a/src/middlewares/Toast/index.js b/src/middlewares/Toast/index.js new file mode 100644 index 0000000..ad7a05c --- /dev/null +++ b/src/middlewares/Toast/index.js @@ -0,0 +1,14 @@ +export default function ToastMiddlewares() { + return function toast(ctx, next) { + if (ctx.toast) return next() + // error success info + ctx.toast = function (type, message) { + ctx.cookies.set("toast", JSON.stringify({ type: type, message: encodeURIComponent(message) }), { + maxAge: 1, + httpOnly: false, + path: "/", + }) + } + return next() + } +} diff --git a/src/middlewares/errorHandler/index.js b/src/middlewares/errorHandler/index.js index 73de3d5..328f79c 100644 --- a/src/middlewares/errorHandler/index.js +++ b/src/middlewares/errorHandler/index.js @@ -1,3 +1,4 @@ +import { logger } from "@/logger" import CommonError from "utils/error/CommonError" // src/plugins/errorHandler.js // 错误处理中间件插件 @@ -32,7 +33,7 @@ export default function errorHandler() { await formatError(ctx, 404, "Resource not found") } } catch (err) { - console.error(err); + logger.error(err) const isDev = process.env.NODE_ENV === "development" if (isDev && err.stack) { console.error(err.stack) @@ -43,7 +44,7 @@ export default function errorHandler() { httpOnly: false, path: "/", }) - ctx.redirect(ctx.path) + ctx.redirect(ctx.path+"?msg="+err.message) return } await formatError(ctx, err.statusCode || 500, err.message || err || "Internal server error", isDev ? err.stack : undefined) diff --git a/src/middlewares/install.js b/src/middlewares/install.js index d54cede..99366d2 100644 --- a/src/middlewares/install.js +++ b/src/middlewares/install.js @@ -8,12 +8,14 @@ import { auth } from "./Auth" import bodyParser from "koa-bodyparser" import Views from "./Views" import Session from "./Session" +import Toast from "./Toast" import { autoRegisterControllers } from "@/utils/ForRegister.js" const __dirname = path.dirname(fileURLToPath(import.meta.url)) const publicPath = resolve(__dirname, "../../public") export default app => { + app.use(Toast()) app.use(ErrorHandler()) app.use(ResponseTime) app.use(Session(app)); diff --git a/src/views/page/register/index.pug b/src/views/page/register/index.pug index 79a5902..34871f5 100644 --- a/src/views/page/register/index.pug +++ b/src/views/page/register/index.pug @@ -77,6 +77,7 @@ block pageContent .register-container .register-title 注册账号 form(action="/register" method="post") + input(type="text" name="randomStr" value=randomStr style="display:none") .form-group label(for="username") 用户名 input(type="text" id="username" name="username" required placeholder="请输入用户名") @@ -86,5 +87,9 @@ block pageContent .form-group label(for="confirm_password") 确认密码 input(type="password" id="confirm_password" name="confirm_password" required placeholder="请再次输入密码") + img(src="/captcha", alt="") + .form-group + label(for="code") 验证码 + input(type="text" id="code" name="code" required placeholder="请输入验证码") button.register-btn(type="submit") 注册 a.login-link(href="/login") 已有账号?去登录 \ No newline at end of file