vpn_based_remote_access_system
Version:
毕设:基于OpenVPN的远程访问系统
639 lines (571 loc) • 26.7 kB
JavaScript
const express = require('express');
const bcrypt = require('bcryptjs');
const sqlite3 = require('sqlite3').verbose();
const router = express.Router();
const fs = require('fs');
const path = require('path');
const { logInfo, logError } = require('./logs')('Userpage');
const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
router.use(express.json());
const MINPORT = config.minPortNumber;
const MAXPORT = config.maxPortNumber;
// 引入代理服务器操作接口
const { ProxyServer } = require('./proxy');
const proxy = require('./proxy');
// 初始化 SQLite 数据库
const db = new sqlite3.Database('./users.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
if (err) {
logError(err.message);
}
logInfo('Connected to the users database.');
});
// 存储用户基本信息的数据表
db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
registered_at DATETIME,
last_login_at DATETIME
)`);
// 存储用户ip以及分配端口号的数据表,同时还记录了服务器的启动时长(分钟)与时间,和服务器的运行状态
db.run(`CREATE TABLE IF NOT EXISTS user_ips (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
ip TEXT NOT NULL,
port INTEGER DEFAULT 0,
serverStartDuration INTEGER DEFAULT 0,
serverStartTime TEXT DEFAULT 'Never Started',
isServerRunning INTEGER DEFAULT 0,
FOREIGN KEY(username) REFERENCES users(username)
)`);
// 身份验证中间件
function isAuthenticated(req, res, next) {
if (req.session && req.session.username) {
return next();
} else {
res.redirect('/index.html');
}
}
router.post('/register', async (req, res) => {
try {
const { username, email, password } = req.body;
const userIP = req.socket.remoteAddress; // 获取用户 IP
const registeredAt = new Date().toLocaleString(); // 获取当前时间作为注册时间
const hashedPassword = await bcrypt.hash(password + registeredAt, 10); // 将注册时间作为盐值,加密密码
// 检查用户名是否已存在
const checkUsernameQuery = `SELECT * FROM users WHERE username = ?`;
db.get(checkUsernameQuery, [username], async function (err, row) {
if (err) {
return res.status(500).send({ message: 'Error checking username' });
}
if (row) {
// 用户名已存在,返回错误信息
return res.status(400).send({ message: 'Username already exists' });
} else {
// 用户名不存在,可以注册,在 users 表中插入新的一行,以添加用户
const userQuery = `INSERT INTO users (username, email, password, registered_at) VALUES (?, ?, ?, ?)`;
db.run(userQuery, [username, email, hashedPassword, registeredAt], function (err) {
if (err) {
return res.status(500).send({ message: 'Error creating the user' });
}
// 在 user_ips 表中插入新的一行,port默认为0,即未分配端口给用户
const ipQuery = `INSERT INTO user_ips (username, ip, port) VALUES (?, ?, ?)`;
db.run(ipQuery, [username, userIP, 0], function (ipErr) {
if (ipErr) {
return res.status(500).send({ message: 'Error creating the user' });
}
res.status(201).send({ message: 'User created', username: username });
});
});
logInfo(`User ${username}(${email}) in ${userIP} has registered.`);
}
});
} catch (error) {
res.status(500).send({ message: 'Error creating the user' });
}
});
router.post('/login', async (req, res) => {
try {
const { loginIdentifier, loginPassword } = req.body; // loginIdentifier 含用户名与邮箱
const userQuery = `SELECT * FROM users WHERE username = ? OR email = ?`;
db.get(userQuery, [loginIdentifier, loginIdentifier], async (err, userRow) => {
if (err) {
return logError(err.message);
}
if (!userRow) {
return res.status(401).send({ message: 'Invalid username or password' });
}
const passwordMatch = await bcrypt.compare(loginPassword + userRow.registered_at, userRow.password);
//console.log(loginPassword + userRow.registered_at);
if (!passwordMatch) {
return res.status(401).send({ message: 'Invalid username or password' });
}
// 登录成功,更新数据表中用户上次登录时间
const lastLoginAt = new Date().toLocaleString(); // 获取当前时间作为上次登录时间
const updateUserQuery = `UPDATE users SET last_login_at = ? WHERE id = ?`;
db.run(updateUserQuery, [lastLoginAt, userRow.id], function (updateErr) {
if (updateErr) {
return logError(updateErr.message);
}
const userIP = req.socket.remoteAddress; // 获取用户当前 IP
// 检查是否存在用户的IP记录
const ipSelectQuery = `SELECT * FROM user_ips WHERE username = ?`;
db.get(ipSelectQuery, [loginIdentifier], (selectErr, ipRow) => {
if (selectErr) {
return logError(selectErr.message);
}
if (ipRow) {
// 如果已存在,更新IP
const updateIpQuery = `UPDATE user_ips SET ip = ? WHERE username = ?`;
db.run(updateIpQuery, [userIP, loginIdentifier], function (updateIpErr) {
if (updateIpErr) {
return logError(updateIpErr.message);
}
// 更新成功,为用户跳转页面
logInfo(`User ${loginIdentifier} (IP: ${userIP}) has logged in.`);
req.session.username = userRow.username; // 将用户名存入session
const redirectPage = (loginIdentifier === 'admin') ? '/admin.html' : '/main.html'; // 根据用户名,判断跳转页面
res.json({ success: true, redirect: redirectPage });
});
} else {
// 如果不存在,在表中插入一行新记录
const insertIpQuery = `INSERT INTO user_ips (username, ip, port) VALUES (?, ?, ?)`;
db.run(insertIpQuery, [loginIdentifier, userIP, 0], function (insertIpErr) {
if (insertIpErr) {
return logError(insertIpErr.message);
}
// 插入成功
logInfo(`User ${loginIdentifier} (IP: ${userIP}) has logged in.`);
req.session.username = userRow.username; // 设置session
const redirectPage = (loginIdentifier === 'admin') ? '/admin.html' : '/main.html'; // 根据用户名,判断跳转页面
res.json({ success: true, redirect: redirectPage });
});
}
});
});
});
} catch (error) {
res.status(500).send({ message: 'Failed in logging.' });
}
});
router.get('/main.html', isAuthenticated, (req, res) => {
res.redirect('/public/main.html');
});
router.get('/getUserInfo', async (req, res) => {
try {
// 构造语句,联合查询用户的登录时间、IP、为之分配的端口号等信息
const query = `SELECT u.username, u.last_login_at, ip.ip, ip.port
FROM users u
JOIN user_ips ip ON u.username = ip.username
WHERE u.username = ?`;
db.get(query, [req.session.username], (err, row) => {
if (err) {
return res.status(500).send({ message: 'Error fetching user information' });
}
if (!row) {
return res.status(404).send({ message: 'User not found' });
}
// 返回用户名、上次登录时间、IP和端口号
//console.dir(row);
res.json({
username: row.username,
last_login_at: row.last_login_at,
ip: row.ip,
port: row.port
});
});
} catch (error) {
res.status(500).send({ message: 'Error fetching user information' });
}
});
// 退出登录
router.get('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
logError(err);
} else {
res.json({ redirect: '/index.html' });
}
});
});
// 注销账号
router.post('/unregister', async (req, res) => {
try {
const username = req.session.username;
//console.log(username);
// 检查用户是否存在
const userExistsQuery = `SELECT * FROM users WHERE username = ?`;
db.get(userExistsQuery, [username], async (err, userRow) => {
if (err) {
//console.log('Error checking user existence')
logError("Error checking user existence:", err);
return res.status(500).json({ success: false, message: 'Internal server error' });
}
if (!userRow) {
//console.log('User does not exist')
return res.json({ success: false, message: 'User does not exist' });
}
// 查询用户端口号
const userPortQuery = `SELECT port FROM user_ips WHERE username = ?`;
db.get(userPortQuery, [username], async (portErr, portRow) => {
if (portErr) {
//console.log('Error retrieving user port')
logError("Error retrieving user port:", portErr);
return res.status(500).json({ success: false, message: 'Internal server error' });
}
if (portRow && portRow.port) {
// 如果用户有分配端口,则关闭对应的服务器
proxy.stopServerByPort(portRow.port);
}
// 分别从用户表与 ip 表中删除用户信息
const deleteUserQuery = `DELETE FROM users WHERE username = ?`;
db.run(deleteUserQuery, [username], async (deleteUserErr) => {
if (deleteUserErr) {
//console.log('Error deleting user')
logError("Error deleting user:", deleteUserErr);
return res.status(500).json({ success: false, message: 'Internal server error' });
}
const deleteIPQuery = `DELETE FROM user_ips WHERE username = ?`;
db.run(deleteIPQuery, [username], async (deleteIPErr) => {
if (deleteIPErr) {
//console.log('Error deleting user IP')
logError("Error deleting user IP:", deleteIPErr);
return res.status(500).json({ success: false, message: 'Internal server error' });
}
logInfo(`User ${username} has unregistered.`);
res.json({ redirect: '/index.html', success: true, message: 'Account successfully unregistered' });
});
});
});
});
} catch (error) {
logError("Error unregistering account:", error);
res.status(500).json({ success: false, message: 'Failed to unregister account' });
}
});
function allocatePort(callback) {
// 生成一个在配置所规定范围内的随机数作为分配给用户的端口号
let randomPort = MINPORT + Math.floor(Math.random() * (MAXPORT - MINPORT + 1));
checkAndAllocatePort(randomPort, callback);
}
function checkAndAllocatePort(port, callback, attempt = 1) {
// 检查端口号是否被占用
const MAX_ATTEMPTS = 100; // 最大尝试次数
db.get(`SELECT COUNT(id) AS count FROM user_ips WHERE port = ?`, [port], (err, row) => {
if (err) {
callback(err, null);
return;
}
// 存在记录,说明端口号已被占用
if (row.count > 0) {
if (attempt < MAX_ATTEMPTS) {
let newPort = port + 1 <= MAXPORT ? port + 1 : MINPORT;
checkAndAllocatePort(newPort, callback, attempt + 1);
} else {
// 达到上限,无法分配端口号
callback(new Error('Unable to allocate a port.'), null);
}
} else {
callback(null, port);
}
});
}
router.post('/allocatePort', (req, res) => {
const username = req.session.username; // 从会话中获取用户名及ip
const ip = req.socket.remoteAddress;
// 首先检查用户是否已有分配的端口
db.get(`SELECT port FROM user_ips WHERE username = ?`, [username], (err, row) => {
if (err) {
res.status(500).send('Server error: ' + err.message);
return;
}
// 用户端口号在给定的范围内,说明用户已分配端口号
if (row && row.port > 0 && row.port >= MINPORT && row.port <= MAXPORT) {
res.json({ message: 'User has already get the port.' });
return;
}
// 没有为用户分配端口,或用户端口号不合适,则为之分配一个新端口
allocatePort((err, port) => {
if (err) {
res.status(500).send('Server error: ' + err.message);
return;
}
// 将新分配的端口信息保存到数据库
db.run(`UPDATE user_ips SET ip = ?, port = ? WHERE username = ?`, [ip, port, username], function (err) {
if (err) {
res.status(500).send('Server error: ' + err.message);
return;
}
if (this.changes === 0) {
res.status(404).send('User not found for port allocation.');
} else {
logInfo(`User ${username} (IP: ${ip}) has got a port of proxy server: ${port}.`);
res.json({ port });
}
});
});
});
});
// 为用户启动代理服务器
router.post('/tryStart', (req, res) => {
const username = req.session.username;
// 查出ip与端口号
db.get(`SELECT ip, port FROM user_ips WHERE username = ?`, [username], (err, row) => {
if (err) {
logError(err.message);
return;
}
if (row.port == 0) {
logError('Port is 0.');
return;
}
if (row) {
logInfo(`User ${username} in ${row.ip} is trying to connect to the port ${row.port}.`);
}
const { serverStartDuration } = req.body;
const proxyServer = new ProxyServer(row.port, serverStartDuration, true); // 创建实例
try {
// 启动服务器
proxyServer.startServer()
.then(() => {
// 服务器启动成功后,将该服务器添加到数组中,并更新数据表
proxy.addServer(proxyServer);
const serverStartTime = new Date().toLocaleString();
// 这里的 isServerRunning 仍然为 0,要等到验证 ip 通过以后才能置1
db.run(`UPDATE user_ips SET serverStartDuration = ?, serverStartTime = ?, isServerRunning = 0 WHERE username = ?`, [serverStartDuration, serverStartTime, username], (updateErr) => {
if (updateErr) {
logError(updateErr.message);
res.json({ success: false, message: 'Failed to update server status.' });
return;
}
res.json({ success: true });
});
})
.catch((error) => {
logError(error);
res.json({ success: false });
});
} catch (error) {
logError("Error starting server:", error);
res.json({ success: false });
}
});
});
// 下载配置文件
router.get('/downloadFile', (req, res) => {
const username = req.session.username; // 用户名
const filePath = 'C:/Users/Administrator/Desktop/client.ovpn'; // 文件路径
const fileName = 'client.ovpn'; // 文件名
const tempFilePath = 'C:/Users/Administrator/Desktop/temp_client.ovpn'; // 临时文件路径
// 从数据库里查出为用户分配的端口,并填写到配置文件中
db.get('SELECT port FROM user_ips WHERE username = ?', [username], (err, row) => {
if (err) {
logError("Error querying user port:", err);
res.status(500).send("Error querying user port");
return;
}
if (!row) {
res.status(404).send("User not found");
return;
}
if (row.port == 0) {
res.status(404).send("Port is 0");
return;
}
// 读取用户配置文件,并将查出的端口号修改至第17行,然后为用户建立下载链接
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
logError("Error reading file:", err);
res.status(500).send("Error reading file");
return;
}
const lines = data.split('\n');
if (lines.length < 17) {
res.status(500).send("Invalid file structure");
return;
}
// 更新第17行,填入为用户生成的端口号
lines[16] = `http-proxy 8.130.173.29 ${row.port} stdin basic`;
const updatedData = lines.join('\n');
// 将端口号写入到临时文件中,发送给用户,然后删除
fs.writeFile(tempFilePath, updatedData, 'utf8', (err) => {
if (err) {
logError("Error writing temporary file:", err);
res.status(500).send("Error writing temporary file");
return;
}
// 发送临时文件
res.download(tempFilePath, fileName, (err) => {
if (err) {
logError("Error sending file:", err);
// 处理错误
}
// 删除临时文件
fs.unlink(tempFilePath, (err) => {
if (err) logError("Error deleting temporary file:", err);
});
});
});
});
});
});
// 管理员页面的用户管理接口
router.get('/adminUsers', (req, res) => {
db.all("SELECT users.username, users.email, user_ips.ip, user_ips.port, users.last_login_at, users.registered_at FROM users INNER JOIN user_ips ON users.username = user_ips.username", [], (err, rows) => {
if (err) {
res.status(400).json({ "error": err.message });
return;
}
res.json({
"message": "success",
"data": rows
})
});
});
// 管理员页面的日志管理接口
router.get('/adminLogs', (req, res) => {
const date = req.query.date || new Date().toISOString().split('T')[0];
const lastLogTime = req.query.lastLogTime;
const logPath = path.join(__dirname, 'logs', `${date}.log`);
if (fs.existsSync(logPath)) {
fs.readFile(logPath, 'utf8', (err, data) => {
if (err) {
res.status(400).json({ "error": err.message });
return;
}
let newLogs = "";
// 记录时间戳,用于实时更新日志时不重复
const lastDateTime = lastLogTime ? new Date(lastLogTime) : new Date(0);
const logs = data.split('\n').filter(line => line);
// 读取日志的每一行,检查其时间戳,如果在所记录的时间戳之后,则存入 newLogs 以集中返回给管理员界面
logs.forEach(log => {
const logTimeMatch = log.match(/\[(.*?)\]/);
if (logTimeMatch) {
const logDateTime = new Date(logTimeMatch[1]);
if (logDateTime > lastDateTime) {
newLogs += log + '\n';
}
}
});
let newLastLogTime = lastLogTime;
if (logs.length > 0) {
const lastLogTimeMatch = logs[logs.length - 1].match(/\[(.*?)\]/); // 从日志的最后一行匹配最后一次更新的时间戳
if (lastLogTimeMatch) {
newLastLogTime = lastLogTimeMatch[1];
}
}
res.json({
"message": "success",
"log": newLogs.trim(),
"newLastLogTime": newLastLogTime
});
});
} else {
res.json({
"message": "log not found",
"log": ""
});
}
});
// 管理员页面的链接管理接口
router.get('/adminUser_ips', (req, res) => {
const sql = "SELECT * FROM user_ips";
db.all(sql, [], (err, rows) => {
if (err) {
res.status(400).json({ "error": err.message });
return;
}
// 数据表里 isServerRunning 的字段不管用,现在只能分别查出时间,和当前时间作比较
// 这段代码弃用,改为直接从 servers 数组里返回所有的服务器实例,再将其端口与数据表内容做比较
/*const running = [];
const notRunning = [];
const currentTime = new Date();
rows.forEach(row => {
// 将 serverStartTime 从字符串转换为 Date 对象
const startTime = new Date(row.serverStartTime.replace(/-/g, "/"));
const duration = row.serverStartDuration;
// 计算相差的总分钟数
const diffMinutes = Math.abs(currentTime - startTime) / (1000 * 60);
// 将服务器分类为正在运行或未运行
if (diffMinutes <= duration) {
running.push(row);
} else {
notRunning.push(row);
}
});*/
// 从 servers 数组获取所有正在运行的服务器,并读取其中的端口号
const runningServers = proxy.getServersList();
const runningServerPorts = runningServers.map(server => server.port);
const running = [];
const notRunning = [];
rows.forEach(row => {
// 用端口号从数据库中查询各服务器是否正在运行
if (runningServerPorts.includes(row.port)) {
running.push(row);
} else {
notRunning.push(row);
}
});
res.json({
"message": "success",
"data": {
"running": running,
"notRunning": notRunning
}
});
});
});
// 用户端更新服务器状态
router.get('/server/status', async (req, res) => {
const port = req.query.port;
db.serialize(async () => {
// 先从数据表中查出端口号对应的服务器
const sql = "SELECT * FROM user_ips WHERE port = ?";
db.get(sql, [port], async (err, row) => {
if (err) {
res.status(400).json({ "error": err.message });
return;
}
// 再从 proxy 接口获取当前正在运行的服务器端口
const runningServers = await proxy.getServersList();
const runningServerPorts = runningServers.map(server => server.port);
// 检查所请求的端口号是否在上述列表中
if (runningServerPorts.includes(parseInt(port))) {
// 服务器正在运行
if (row) {
// 处理时间
const startTime = new Date(row.serverStartTime.replace(/-/g, "/"));
const duration = row.serverStartDuration;
const currentTime = new Date();
// 计算剩余时间
const elapsedTime = (currentTime - startTime) / (1000 * 60);
const remainingTime = duration - elapsedTime;
if (remainingTime > 0) {
// 如果剩余时间大于0,返回剩余时间
res.json({ status: `剩余${Math.round(remainingTime)}分钟` });
} else {
// 剩余时间不大于0,服务器处于关闭状态
res.json({ status: "关闭" });
}
} else {
res.json({ status: "关闭" });
}
} else {
res.json({ status: "关闭" });
}
});
});
});
// 用户端关闭服务器
router.post('/server/stop', async (req, res) => {
const { port } = req.body;
try {
await proxy.stopServerByPort(parseInt(port));
res.json({ message: 'Server has been stopped' });
} catch (error) {
console.error("Error stopping server:", error);
res.status(500).json({ message: 'Error stopping server' });
}
});
module.exports = router;