1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-10-22 02:03:11 +00:00
ClassworksKV/middleware/tokenAuth.js
2025-10-02 12:07:50 +08:00

112 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient } from "@prisma/client";
import { verifyDevicePassword } from "../utils/crypto.js";
import errors from "../utils/errors.js";
const prisma = new PrismaClient();
/**
* Token认证中间件
*
* 从请求中提取token验证后将设备信息注入到res.locals
* 同时将deviceId注入到req.params以便后续路由使用
*
* Token可通过以下方式提供
* 1. Authorization header: Bearer <token>
* 2. Query参数: ?token=<token>
* 3. Body: {"token": "<token>"}
*/
export const tokenAuth = errors.catchAsync(async (req, res, next) => {
let token;
// 尝试从 headers, query, body 中获取 token
if (req.headers.authorization && req.headers.authorization.startsWith("Bearer ")) {
token = req.headers.authorization.split(" ")[1];
} else if (req.query.token) {
token = req.query.token;
} else if (req.body.token) {
token = req.body.token;
}
if (!token) {
return next(errors.createError(401, "未提供身份验证令牌"));
}
const appInstall = await prisma.appInstall.findUnique({
where: { token },
include: {
app: true,
device: true,
},
});
if (!appInstall) {
return next(errors.createError(401, "无效的身份验证令牌"));
}
// 将认证信息存储到res.locals
res.locals.appInstall = appInstall;
res.locals.app = appInstall.app;
res.locals.device = appInstall.device;
res.locals.deviceId = appInstall.device.id;
// 将deviceId注入到req.params向后兼容某些路由可能需要namespace参数
req.params.namespace = appInstall.device.uuid;
req.params.deviceId = appInstall.device.id;
next();
});
/**
* 写权限验证中间件
*
* 依赖于deviceMiddleware必须在其后使用
* 验证设备密码和写权限
*
* 逻辑:
* 1. 如果设备没有设置密码,直接允许写入
* 2. 如果设备设置了密码:
* - 验证提供的密码是否正确
* - 密码正确则允许写入
* - 密码错误或未提供则拒绝写入
*
* 使用方式:
* router.post('/path', deviceMiddleware, requireWriteAuth, handler)
* router.put('/path', deviceMiddleware, requireWriteAuth, handler)
* router.delete('/path', deviceMiddleware, requireWriteAuth, handler)
*/
export const requireWriteAuth = errors.catchAsync(async (req, res, next) => {
const device = res.locals.device;
if (!device) {
return next(errors.createError(500, "设备信息未加载请确保使用了deviceMiddleware"));
}
// 如果设备没有设置密码,直接通过
if (!device.password) {
return next();
}
// 设备有密码,需要验证
const providedPassword = req.body.password || req.query.password;
if (!providedPassword) {
return res.status(401).json({
statusCode: 401,
message: "此操作需要密码",
passwordHint: device.passwordHint,
});
}
// 验证密码
const isValid = await verifyDevicePassword(providedPassword, device.password);
if (!isValid) {
return res.status(401).json({
statusCode: 401,
message: "密码错误",
});
}
// 密码正确,继续
next();
});