This commit is contained in:
2025-07-29 15:36:25 -07:00
commit 0c481c7a0e
29 changed files with 6682 additions and 0 deletions

39
middleware/auth.js Normal file
View File

@ -0,0 +1,39 @@
const jwt = require('jsonwebtoken');
// 生成JWT token
const generateToken = (userId, username) => {
return jwt.sign(
{ userId, username },
process.env.JWT_SECRET || 'your_jwt_secret_key_here',
{ expiresIn: '24h' }
);
};
// 验证JWT token中间件
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key_here', (err, user) => {
if (err) {
return res.status(403).json({
success: false,
message: '访问令牌无效'
});
}
req.user = user;
next();
});
};
module.exports = {
generateToken,
authenticateToken
};

118
middleware/oauth.js Normal file
View File

@ -0,0 +1,118 @@
const OAuthToken = require('../models/OAuthToken');
// OAuth访问令牌验证中间件
const authenticateOAuthToken = async (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
const tokenData = await OAuthToken.validateAccessToken(token);
if (!tokenData) {
return res.status(401).json({
success: false,
message: '访问令牌无效或已过期'
});
}
req.oauth = {
token: tokenData.token,
clientId: tokenData.client_id,
userId: tokenData.user_id,
scopes: tokenData.scopes,
username: tokenData.username,
email: tokenData.email
};
next();
} catch (error) {
console.error('OAuth令牌验证失败:', error);
return res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
// 检查OAuth权限范围
const requireScope = (requiredScope) => {
return (req, res, next) => {
if (!req.oauth) {
return res.status(401).json({
success: false,
message: '需要OAuth认证'
});
}
if (!req.oauth.scopes.includes(requiredScope)) {
return res.status(403).json({
success: false,
message: `需要权限范围: ${requiredScope}`
});
}
next();
};
};
// 检查多个权限范围(任一即可)
const requireAnyScope = (requiredScopes) => {
return (req, res, next) => {
if (!req.oauth) {
return res.status(401).json({
success: false,
message: '需要OAuth认证'
});
}
const hasAnyScope = requiredScopes.some(scope =>
req.oauth.scopes.includes(scope)
);
if (!hasAnyScope) {
return res.status(403).json({
success: false,
message: `需要权限范围: ${requiredScopes.join(' 或 ')}`
});
}
next();
};
};
// 检查所有权限范围
const requireAllScopes = (requiredScopes) => {
return (req, res, next) => {
if (!req.oauth) {
return res.status(401).json({
success: false,
message: '需要OAuth认证'
});
}
const hasAllScopes = requiredScopes.every(scope =>
req.oauth.scopes.includes(scope)
);
if (!hasAllScopes) {
return res.status(403).json({
success: false,
message: `需要所有权限范围: ${requiredScopes.join(', ')}`
});
}
next();
};
};
module.exports = {
authenticateOAuthToken,
requireScope,
requireAnyScope,
requireAllScopes
};

56
middleware/validation.js Normal file
View File

@ -0,0 +1,56 @@
const { body, validationResult } = require('express-validator');
// 注册验证规则
const registerValidation = [
body('username')
.isLength({ min: 3, max: 50 })
.withMessage('用户名长度必须在3-50个字符之间')
.matches(/^[a-zA-Z0-9_]+$/)
.withMessage('用户名只能包含字母、数字和下划线'),
body('email')
.isEmail()
.withMessage('请输入有效的邮箱地址')
.normalizeEmail(),
body('password')
.isLength({ min: 6 })
.withMessage('密码长度至少6个字符')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('密码必须包含至少一个小写字母、一个大写字母和一个数字')
];
// 登录验证规则
const loginValidation = [
body('username')
.notEmpty()
.withMessage('用户名不能为空'),
body('password')
.notEmpty()
.withMessage('密码不能为空')
];
// 验证结果处理中间件
const handleValidationErrors = (req, res, next) => {
const errors = validationResult(req);
// console.log(req.body);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入验证失败',
errors: errors.array().map(error => ({
field: error.path,
message: error.msg
}))
});
}
next();
};
module.exports = {
registerValidation,
loginValidation,
handleValidationErrors
};