1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-10-22 18:33:11 +00:00
ClassworksKV/middleware/uuidAuth.js
2025-10-03 21:22:18 +08:00

131 lines
3.3 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.

/**
* UUID+密码/JWT混合认证中间件
*
* 1. 必须提供UUID读取设备信息并存储到res.locals
* 2. 验证密码或账户JWT二选一
* 3. 适用于需要设备上下文的接口
*/
import { PrismaClient } from "@prisma/client";
import errors from "../utils/errors.js";
import { verifyToken as verifyAccountJWT } from "../utils/jwt.js";
import { verifyDevicePassword } from "../utils/crypto.js";
const prisma = new PrismaClient();
/**
* UUID+密码/JWT混合认证中间件
*/
export const uuidAuth = async (req, res, next) => {
try {
// 1. 获取UUID必需
const uuid = extractUuid(req);
if (!uuid) {
return next(errors.createError(400, "需要提供设备UUID"));
}
// 2. 查找设备并存储到locals
const device = await prisma.device.findUnique({
where: { uuid },
});
if (!device) {
return next(errors.createError(404, "设备不存在"));
}
// 存储设备信息到locals
res.locals.device = device;
res.locals.deviceId = device.id;
// 3. 验证密码或JWT二选一
const password = extractPassword(req);
const jwt = extractJWT(req);
if (jwt) {
// 验证账户JWT
try {
const accountPayload = await verifyAccountJWT(jwt);
const account = await prisma.account.findUnique({
where: { id: accountPayload.accountId },
include: {
devices: {
where: { uuid },
select: { id: true }
}
}
});
if (!account) {
return next(errors.createError(401, "账户不存在"));
}
// 检查设备是否绑定到此账户
if (account.devices.length === 0) {
return next(errors.createError(403, "设备未绑定到此账户"));
}
res.locals.account = account;
res.locals.isAccountOwner = true; // 标记为账户拥有者
return next();
} catch (error) {
return next(errors.createError(401, "无效的JWT token"));
}
} else if (password) {
// 验证设备密码
if (!device.password) {
return next(); // 如果设备未设置密码,允许无密码访问
}
const isValid = await verifyDevicePassword(password, device.password);
if (!isValid) {
return next(errors.createError(401, "密码错误"));
}
return next();
} else {
// 如果设备未设置密码,允许无密码访问
if (!device.password) {
return next();
}
return next(errors.createError(401, "需要提供密码或JWT token"));
}
} catch (error) {
next(error);
}
};
/**
* 从请求中提取UUID
*/
function extractUuid(req) {
return (
req.headers["x-device-uuid"] ||
req.query.uuid ||
req.params.uuid ||
req.params.deviceUuid ||
(req.body && req.body.uuid) ||
(req.body && req.body.deviceUuid)
);
}
/**
* 从请求中提取密码
*/
function extractPassword(req) {
return (
req.headers["x-device-password"] ||
req.query.password ||
req.query.currentPassword
);
}
/**
* 从请求中提取JWT
*/
function extractJWT(req) {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return null;
}