diff --git a/Login/login.cpp b/Login/login.cpp index 39bff6a..e89e0f7 100644 --- a/Login/login.cpp +++ b/Login/login.cpp @@ -11,17 +11,20 @@ #include #include #include +#include +#include +#include -using json = nlohmann::json; +using json = nlohmann::json; namespace fs = std::filesystem; -std::string generateSalt(const std::string& username) +std::string generateSalt(const std::string &username) { // 使用用户名生成盐值 return username + "XNSim_Salt_Key"; } -std::string encryptPassword(const std::string& password, const std::string& salt) +std::string encryptPassword(const std::string &password, const std::string &salt) { // 将密码和盐值组合 std::string saltedPassword = password; @@ -31,7 +34,7 @@ std::string encryptPassword(const std::string& password, const std::string& salt // 使用SHA-256算法对加盐密码进行加密 unsigned char hash[SHA256_DIGEST_LENGTH]; - EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); EVP_DigestUpdate(ctx, saltedPassword.c_str(), saltedPassword.length()); EVP_DigestFinal_ex(ctx, hash, NULL); @@ -39,43 +42,77 @@ std::string encryptPassword(const std::string& password, const std::string& salt // 转换为十六进制字符串 std::stringstream ss; - for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(hash[i]); } return ss.str(); } -extern "C" LOGIN_EXPORT int validateUser(const void* username_buffer, size_t username_length, - const void* password_buffer, size_t password_length) +// Base64编码函数 +std::string base64_encode(const unsigned char *data, size_t length) +{ + const char *base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string ret; + ret.reserve(((length + 2) / 3) * 4); + + for (size_t i = 0; i < length; i += 3) { + unsigned char octet_a = i < length ? data[i] : 0; + unsigned char octet_b = i + 1 < length ? data[i + 1] : 0; + unsigned char octet_c = i + 2 < length ? data[i + 2] : 0; + + unsigned char triple = (octet_a << 16) + (octet_b << 8) + octet_c; + + ret.push_back(base64_chars[(triple >> 18) & 0x3F]); + ret.push_back(base64_chars[(triple >> 12) & 0x3F]); + ret.push_back(base64_chars[(triple >> 6) & 0x3F]); + ret.push_back(base64_chars[triple & 0x3F]); + } + + // 添加填充 + switch (length % 3) { + case 1: + ret[ret.size() - 2] = '='; + ret[ret.size() - 1] = '='; + break; + case 2: + ret[ret.size() - 1] = '='; + break; + } + + return ret; +} + +extern "C" LOGIN_EXPORT int validateUser(const char *username_buffer, size_t username_length, + const char *password_buffer, size_t password_length) { try { - std::string username_str(static_cast(username_buffer), username_length); - std::string password_str(static_cast(password_buffer), password_length); + std::string username_str(username_buffer, username_length); + std::string password_str(password_buffer, password_length); if (username_str.empty() || password_str.empty()) { return -1; } - std::string salt = generateSalt(username_str); + std::string salt = generateSalt(username_str); std::string encryptedPassword = encryptPassword(password_str, salt); // 获取环境变量 - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } int userId = -1; - sqlite3_stmt* stmt; - const char* queryStr = "SELECT * FROM users WHERE username = ? AND password = ?"; - + sqlite3_stmt *stmt; + const char *queryStr = "SELECT * FROM users WHERE username = ? AND password = ?"; + if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_text(stmt, 1, username_str.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, encryptedPassword.c_str(), -1, SQLITE_STATIC); @@ -90,61 +127,80 @@ extern "C" LOGIN_EXPORT int validateUser(const void* username_buffer, size_t use sqlite3_close(db); return userId; - } catch (const std::exception&) { + } catch (const std::exception &) { return -1; } } -extern "C" LOGIN_EXPORT const char* getUserInfo(int user_id) +extern "C" LOGIN_EXPORT int getUserInfo(int user_id, char *result, int result_length) { try { - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { - return nullptr; + std::cout << "xnCorePath is null" << std::endl; + return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { - return nullptr; + std::cout << "sqlite3_open failed" << std::endl; + return -1; } - char* result = nullptr; - sqlite3_stmt* stmt; - const char* queryStr = "SELECT * FROM users WHERE id = ?"; + sqlite3_stmt *stmt; + const char *queryStr = "SELECT * FROM users WHERE id = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int(stmt, 1, user_id); if (sqlite3_step(stmt) == SQLITE_ROW) { json userInfo; - userInfo["id"] = sqlite3_column_int(stmt, 0); - userInfo["username"] = reinterpret_cast(sqlite3_column_text(stmt, 1)); + userInfo["id"] = sqlite3_column_int(stmt, 0); + userInfo["username"] = reinterpret_cast(sqlite3_column_text(stmt, 1)); userInfo["access_level"] = sqlite3_column_int(stmt, 3); - userInfo["full_name"] = reinterpret_cast(sqlite3_column_text(stmt, 4)); - userInfo["phone"] = reinterpret_cast(sqlite3_column_text(stmt, 5)); - userInfo["email"] = reinterpret_cast(sqlite3_column_text(stmt, 6)); - userInfo["department"] = reinterpret_cast(sqlite3_column_text(stmt, 7)); - userInfo["position"] = reinterpret_cast(sqlite3_column_text(stmt, 8)); + userInfo["full_name"] = + reinterpret_cast(sqlite3_column_text(stmt, 4)); + userInfo["phone"] = reinterpret_cast(sqlite3_column_text(stmt, 5)); + userInfo["email"] = reinterpret_cast(sqlite3_column_text(stmt, 6)); + userInfo["department"] = + reinterpret_cast(sqlite3_column_text(stmt, 7)); + userInfo["position"] = reinterpret_cast(sqlite3_column_text(stmt, 8)); + + // 处理二进制数据 + const void *blob_data = sqlite3_column_blob(stmt, 9); + int blob_size = sqlite3_column_bytes(stmt, 9); + if (blob_data && blob_size > 0) { + userInfo["icon"] = + base64_encode(static_cast(blob_data), blob_size); + } else { + userInfo["icon"] = nullptr; // 使用null值代替空字符串 + } std::string jsonData = userInfo.dump(); - result = new char[jsonData.size() + 1]; - std::strcpy(result, jsonData.c_str()); + if (result_length >= jsonData.size() + 1) { + std::strcpy(result, jsonData.c_str()); + return 0; + } else { + std::cout << "result_length: " << result_length << std::endl; + return -1; + } } sqlite3_finalize(stmt); } sqlite3_close(db); - return result; + return 0; - } catch (const std::exception&) { - return nullptr; + } catch (const std::exception &) { + std::cout << "getUserInfo failed" << std::endl; + return -1; } } -extern "C" LOGIN_EXPORT void freeUserInfo(const char* ptr) +extern "C" LOGIN_EXPORT void freeUserInfo(const char *ptr) { if (ptr) { delete[] ptr; @@ -166,23 +222,27 @@ int checkUsernameExists(const void *username_buffer, size_t username_length) return -1; } - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - std::string connectionName = "usercheck_" + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string connectionName = + "usercheck_" + + std::to_string(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()); int result = -1; { - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } - sqlite3_stmt* stmt; - const char* queryStr = "SELECT COUNT(*) FROM users WHERE username = ?"; + sqlite3_stmt *stmt; + const char *queryStr = "SELECT COUNT(*) FROM users WHERE username = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_text(stmt, 1, username_str.c_str(), -1, SQLITE_STATIC); @@ -204,18 +264,15 @@ int checkUsernameExists(const void *username_buffer, size_t username_length) } // 注册新用户 -extern "C" LOGIN_EXPORT int registerUser(const void *username_buffer, size_t username_length, - const void *password_buffer, size_t password_length, - const void *userinfo_buffer, size_t userinfo_length) +extern "C" LOGIN_EXPORT int registerUser(const char *username_buffer, int username_length, + const char *password_buffer, int password_length, + const char *userinfo_buffer, int userinfo_length) { try { // 转换输入参数 - const char *username_data = static_cast(username_buffer); - const char *password_data = static_cast(password_buffer); - const char *userinfo_data = static_cast(userinfo_buffer); - - std::string username_str(username_data, username_length); - std::string password_str(password_data, password_length); + std::string username_str(username_buffer, username_length); + std::string password_str(password_buffer, password_length); + std::string userinfo_str(userinfo_buffer, userinfo_length); // 验证用户名和密码非空 if (username_str.empty()) { @@ -233,8 +290,8 @@ extern "C" LOGIN_EXPORT int registerUser(const void *username_buffer, size_t use // 解析用户信息JSON json userInfo; try { - userInfo = json::parse(std::string(userinfo_data, userinfo_length)); - } catch (const json::parse_error&) { + userInfo = json::parse(userinfo_str); + } catch (const json::parse_error &) { return -3; // Invalid user info format } @@ -242,39 +299,49 @@ extern "C" LOGIN_EXPORT int registerUser(const void *username_buffer, size_t use int accessLevel = 0; // 生成加密密码 - std::string salt = generateSalt(username_str); + std::string salt = generateSalt(username_str); std::string encryptedPassword = encryptPassword(password_str, salt); // 连接数据库 - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - std::string connectionName = "userreg_" + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string connectionName = + "userreg_" + + std::to_string(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()); int newUserId = -1; { - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } - sqlite3_stmt* stmt; - const char* queryStr = "INSERT INTO users (username, password, access_level, full_name, phone, " - "email, department, position) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + sqlite3_stmt *stmt; + const char *queryStr = + "INSERT INTO users (username, password, access_level, full_name, phone, " + "email, department, position) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_text(stmt, 1, username_str.c_str(), -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, encryptedPassword.c_str(), -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 3, accessLevel); - sqlite3_bind_text(stmt, 4, userInfo["full_name"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 5, userInfo["phone"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 6, userInfo["email"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 7, userInfo["department"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 8, userInfo["position"].get().c_str(), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 4, userInfo["full_name"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 5, userInfo["phone"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 6, userInfo["email"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 7, userInfo["department"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 8, userInfo["position"].get().c_str(), -1, + SQLITE_STATIC); if (sqlite3_step(stmt) == SQLITE_DONE) { newUserId = sqlite3_last_insert_rowid(db); @@ -293,49 +360,51 @@ extern "C" LOGIN_EXPORT int registerUser(const void *username_buffer, size_t use } // 修改密码 -extern "C" LOGIN_EXPORT int changePassword(int user_id, const void *old_password_buffer, - size_t old_password_length, - const void *new_password_buffer, - size_t new_password_length) +extern "C" LOGIN_EXPORT int changePassword(int user_id, const char *old_password_buffer, + int old_password_length, const char *new_password_buffer, + int new_password_length) { try { - const char *old_password_data = static_cast(old_password_buffer); - const char *new_password_data = static_cast(new_password_buffer); - - std::string old_password_str(old_password_data, old_password_length); - std::string new_password_str(new_password_data, new_password_length); + std::string old_password_str(old_password_buffer, old_password_length); + std::string new_password_str(new_password_buffer, new_password_length); if (old_password_str.empty() || new_password_str.empty()) { return -1; } - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - std::string connectionName = "changepwd_" + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string connectionName = + "changepwd_" + + std::to_string(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()); { - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } // 首先验证旧密码 - sqlite3_stmt* stmt; - const char* queryStr = "SELECT username, password FROM users WHERE id = ?"; + sqlite3_stmt *stmt; + const char *queryStr = "SELECT username, password FROM users WHERE id = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int(stmt, 1, user_id); if (sqlite3_step(stmt) == SQLITE_ROW) { - std::string username = reinterpret_cast(sqlite3_column_text(stmt, 0)); - std::string storedPassword = reinterpret_cast(sqlite3_column_text(stmt, 1)); + std::string username = + reinterpret_cast(sqlite3_column_text(stmt, 0)); + std::string storedPassword = + reinterpret_cast(sqlite3_column_text(stmt, 1)); // 验证旧密码 - std::string salt = generateSalt(username); + std::string salt = generateSalt(username); std::string encryptedOldPassword = encryptPassword(old_password_str, salt); if (encryptedOldPassword != storedPassword) { @@ -349,7 +418,7 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const void *old_password // 更新密码 sqlite3_finalize(stmt); - stmt = nullptr; + stmt = nullptr; queryStr = "UPDATE users SET password = ? WHERE id = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { @@ -370,47 +439,50 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const void *old_password sqlite3_close(db); } - return -1; // Default error return - } catch (const std::exception&) { + return -1; // Default error return + } catch (const std::exception &) { return -1; } } // 更新用户信息 -extern "C" LOGIN_EXPORT int updateUserInfo(int user_id, const void *userinfo_buffer, - size_t userinfo_length) +extern "C" LOGIN_EXPORT int updateUserInfo(int user_id, const char *userinfo_buffer, + int userinfo_length) { try { - const char *userinfo_data = static_cast(userinfo_buffer); - std::string userinfo_str(userinfo_data, userinfo_length); + std::string userinfo_str(userinfo_buffer, userinfo_length); // 解析用户信息JSON json userInfo; try { userInfo = json::parse(userinfo_str); - } catch (const json::parse_error&) { + } catch (const json::parse_error &) { return -1; // Invalid user info format } // 连接数据库 - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - std::string connectionName = "userupdate_" + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string connectionName = + "userupdate_" + + std::to_string(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()); int result = -1; { - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } // 首先检查用户是否存在 - sqlite3_stmt* stmt; - const char* checkQueryStr = "SELECT id FROM users WHERE id = ?"; + sqlite3_stmt *stmt; + const char *checkQueryStr = "SELECT id FROM users WHERE id = ?"; if (sqlite3_prepare_v2(db, checkQueryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int(stmt, 1, user_id); @@ -424,16 +496,22 @@ extern "C" LOGIN_EXPORT int updateUserInfo(int user_id, const void *userinfo_buf } stmt = nullptr; - const char* queryStr = "UPDATE users SET full_name = ?, phone = ?, email = ?, department = ?, " - "position = ? " - "WHERE id = ?"; + const char *queryStr = + "UPDATE users SET full_name = ?, phone = ?, email = ?, department = ?, " + "position = ? " + "WHERE id = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { - sqlite3_bind_text(stmt, 1, userInfo["full_name"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, userInfo["phone"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 3, userInfo["email"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 4, userInfo["department"].get().c_str(), -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 5, userInfo["position"].get().c_str(), -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 1, userInfo["full_name"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, userInfo["phone"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, userInfo["email"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 4, userInfo["department"].get().c_str(), -1, + SQLITE_STATIC); + sqlite3_bind_text(stmt, 5, userInfo["position"].get().c_str(), -1, + SQLITE_STATIC); sqlite3_bind_int(stmt, 6, user_id); if (sqlite3_step(stmt) == SQLITE_DONE) { @@ -462,24 +540,28 @@ extern "C" LOGIN_EXPORT int updateUserAccessLevel(int user_id, int access_level) } // 连接数据库 - const char* xnCorePath = std::getenv("XNCore"); + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { return -1; } fs::path dbPath = fs::path(xnCorePath) / "database" / "XNSim.db"; - std::string connectionName = "useraccess_" + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string connectionName = + "useraccess_" + + std::to_string(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()); int result = -1; { - sqlite3* db; + sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { return -1; } // 首先检查用户是否存在 - sqlite3_stmt* stmt; - const char* checkQueryStr = "SELECT id FROM users WHERE id = ?"; + sqlite3_stmt *stmt; + const char *checkQueryStr = "SELECT id FROM users WHERE id = ?"; if (sqlite3_prepare_v2(db, checkQueryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int(stmt, 1, user_id); @@ -492,8 +574,8 @@ extern "C" LOGIN_EXPORT int updateUserAccessLevel(int user_id, int access_level) sqlite3_finalize(stmt); } - stmt = nullptr; - const char* queryStr = "UPDATE users SET access_level = ? WHERE id = ?"; + stmt = nullptr; + const char *queryStr = "UPDATE users SET access_level = ? WHERE id = ?"; if (sqlite3_prepare_v2(db, queryStr, -1, &stmt, nullptr) == SQLITE_OK) { sqlite3_bind_int(stmt, 1, access_level); diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index eace14b..b04d6e7 100644 Binary files a/Release/database/XNSim.db and b/Release/database/XNSim.db differ diff --git a/XNSimHtml/components/qa-component.js b/XNSimHtml/components/qa-component.js index a97ad1a..2ac92fe 100644 --- a/XNSimHtml/components/qa-component.js +++ b/XNSimHtml/components/qa-component.js @@ -18,10 +18,14 @@ class QAComponent extends HTMLElement { async checkUserAccess() { try { - const userInfo = localStorage.getItem('userInfo'); - if (userInfo) { - const user = JSON.parse(userInfo); - this.accessLevel = user.access_level || 0; + const response = await fetch('/api/check-auth', { + credentials: 'include' + }); + const result = await response.json(); + if (result.success) { + this.accessLevel = result.user.access_level || 0; + } else { + this.accessLevel = 0; } } catch (error) { console.error('获取用户权限失败:', error); @@ -578,7 +582,6 @@ class QAComponent extends HTMLElement { } try { - const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const response = await fetch('/api/qa/questions', { method: 'POST', headers: { @@ -586,9 +589,9 @@ class QAComponent extends HTMLElement { }, body: JSON.stringify({ title, - content, - userInfo - }) + content + }), + credentials: 'include' }); const data = await response.json(); @@ -607,16 +610,15 @@ class QAComponent extends HTMLElement { async addAnswer(questionId, content) { try { - const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const response = await fetch(`/api/qa/questions/${questionId}/answers`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - content, - userInfo - }) + content + }), + credentials: 'include' }); const data = await response.json(); @@ -633,13 +635,12 @@ class QAComponent extends HTMLElement { async deleteQuestion(questionId) { try { - const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const response = await fetch(`/api/qa/questions/${questionId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userInfo }) + credentials: 'include' }); const data = await response.json(); @@ -656,13 +657,12 @@ class QAComponent extends HTMLElement { async deleteAnswer(answerId) { try { - const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const response = await fetch(`/api/qa/answers/${answerId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userInfo }) + credentials: 'include' }); const data = await response.json(); diff --git a/XNSimHtml/components/user-info.js b/XNSimHtml/components/user-info.js index 06e3fee..6018cc7 100644 --- a/XNSimHtml/components/user-info.js +++ b/XNSimHtml/components/user-info.js @@ -257,17 +257,6 @@ class UserInfo extends HTMLElement { const result = await response.json(); if (result.success) { - // 只存储非敏感的用户信息 - localStorage.setItem('userInfo', JSON.stringify({ - username: result.user.username, - access_level: result.user.access_level, - full_name: result.user.full_name, - phone: result.user.phone, - email: result.user.email, - department: result.user.department, - position: result.user.position - })); - this.updateUserInfo(result.user); } else { document.getElementById('authContainer').style.display = 'block'; @@ -345,9 +334,6 @@ class UserInfo extends HTMLElement { }); if (response.ok) { - // 清除本地存储的用户信息 - localStorage.removeItem('userInfo'); - // 获取认证容器和主容器 const authContainer = document.getElementById('authContainer'); const mainContainer = document.getElementById('mainContainer'); diff --git a/XNSimHtml/main.html b/XNSimHtml/main.html index 4b65df4..271cfef 100644 --- a/XNSimHtml/main.html +++ b/XNSimHtml/main.html @@ -281,13 +281,6 @@ const result = await response.json(); if (result.success) { - // 只存储非敏感的用户信息 - localStorage.setItem('userInfo', JSON.stringify({ - username: result.user.username, - role: result.user.role, - // 其他非敏感信息... - })); - // 先初始化主页面 initializeMainPage(); @@ -421,8 +414,6 @@ method: 'POST', credentials: 'include' }).then(() => { - // 清除本地存储的用户信息 - localStorage.removeItem('userInfo'); // 清除所有标签页 tabsContainer.clearAllTabs(); diff --git a/XNSimHtml/routes/auth.js b/XNSimHtml/routes/auth.js index b399bab..137b4b1 100644 --- a/XNSimHtml/routes/auth.js +++ b/XNSimHtml/routes/auth.js @@ -1,28 +1,6 @@ const express = require('express'); const router = express.Router(); const { loginLib, stringToBuffer } = require('../utils/xnCoreService'); -const jwt = require('jsonwebtoken'); - -// JWT密钥 -const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; - -// 生成token -const generateToken = (user) => { - return jwt.sign( - { - id: user.id, - username: user.username, - access_level: user.access_level, - full_name: user.full_name, - phone: user.phone, - email: user.email, - department: user.department, - position: user.position - }, - JWT_SECRET, - { expiresIn: '30m' } - ); -}; // 登录API路由 router.post('/login', (req, res) => { @@ -50,34 +28,37 @@ router.post('/login', (req, res) => { if (userId > 0) { try { - const userInfoStr = loginLib.getUserInfo(userId); - let userInfo; - try { - userInfo = JSON.parse(userInfoStr); - - // 生成token - const token = generateToken(userInfo); - - // 设置HttpOnly Cookie - res.cookie('authToken', token, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', // 在生产环境中使用HTTPS - sameSite: 'strict', - maxAge: 30 * 60 * 1000 // 30分钟 - }); - - // 返回用户信息时排除密码字段 - const { password, ...userInfoWithoutPassword } = userInfo; - - res.json({ - success: true, - message: '登录成功', - user: userInfoWithoutPassword // 返回不包含密码的用户信息 - }); - } finally { - if (userInfoStr) { - //loginLib.freeUserInfo(userInfoStr); + const userInfoBuffer = Buffer.alloc(1024); + const userInfoState = loginLib.getUserInfo(userId, userInfoBuffer, userInfoBuffer.length); + + if (userInfoState === 0) { + const zeroIndex = userInfoBuffer.indexOf(0); + const userInfoStr = userInfoBuffer.toString('utf8', 0, zeroIndex >= 0 ? zeroIndex : userInfoBuffer.length); + try { + const userInfo = JSON.parse(userInfoStr); + + // 设置 session + req.session.user = userInfo; + + res.json({ + success: true, + message: '登录成功', + user: userInfo + }); + } catch (parseError) { + console.error('解析用户信息失败:', parseError); + res.status(500).json({ + success: false, + message: '解析用户信息失败', + error: parseError.message + }); } + } else { + res.status(501).json({ + success: false, + message: '获取用户信息失败', + error: '用户信息获取失败,错误码:' + userInfoState + }); } } catch (userInfoError) { console.error('获取用户信息失败:', userInfoError); @@ -88,10 +69,19 @@ router.post('/login', (req, res) => { }); } } else { - res.json({ success: false, message: '用户名或密码错误' }); + res.status(401).json({ + success: false, + message: '用户名或密码错误', + error: '认证失败,错误码:' + userId + }); } } catch (callError) { - throw callError; + console.error('调用动态库失败:', callError); + res.status(500).json({ + success: false, + message: '调用动态库失败', + error: callError.message + }); } } catch (error) { console.error('登录处理过程出错:', error); @@ -105,37 +95,31 @@ router.post('/login', (req, res) => { // 登出API路由 router.post('/logout', (req, res) => { - res.clearCookie('authToken'); - res.json({ - success: true, - message: '已安全退出登录' + req.session.destroy((err) => { + if (err) { + return res.status(500).json({ + success: false, + message: '登出失败' + }); + } + res.json({ + success: true, + message: '已安全退出登录' + }); }); }); // 认证检查API路由 router.get('/check-auth', (req, res) => { - try { - const token = req.cookies.authToken; - - if (!token) { - return res.json({ - success: false, - message: '未登录' - }); - } - - // 验证token - const user = jwt.verify(token, JWT_SECRET); - + if (req.session.user) { res.json({ success: true, - user: user // 返回完整的用户信息(不包含密码) + user: req.session.user }); - } catch (error) { - console.error('认证检查错误:', error); + } else { res.json({ success: false, - message: '登录已过期' + message: '未登录' }); } }); diff --git a/XNSimHtml/routes/qa.js b/XNSimHtml/routes/qa.js index e7a9c68..2829488 100644 --- a/XNSimHtml/routes/qa.js +++ b/XNSimHtml/routes/qa.js @@ -8,6 +8,17 @@ const { deleteAnswer } = require('../utils/qa-utils'); +// 认证中间件 +const authMiddleware = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: '未登录' + }); + } + next(); +}; + // 获取所有问题 router.get('/questions', (req, res) => { try { @@ -20,16 +31,16 @@ router.get('/questions', (req, res) => { }); // 创建新问题 -router.post('/questions', (req, res) => { +router.post('/questions', authMiddleware, (req, res) => { try { const { title, content } = req.body; - const userInfo = req.body.userInfo || {}; + const username = req.session.user.username; if (!title || !content) { return res.status(400).json({ success: false, message: '标题和内容不能为空' }); } - const result = createQuestion(title, content, userInfo.username); + const result = createQuestion(title, content, username); res.status(201).json(result); } catch (error) { console.error('创建问题失败:', error); @@ -38,22 +49,23 @@ router.post('/questions', (req, res) => { }); // 添加回答 -router.post('/questions/:questionId/answers', (req, res) => { +router.post('/questions/:questionId/answers', authMiddleware, (req, res) => { try { const { questionId } = req.params; const { content } = req.body; - const userInfo = req.body.userInfo || {}; + const username = req.session.user.username; + const accessLevel = req.session.user.access_level; if (!content) { return res.status(400).json({ success: false, message: '回答内容不能为空' }); } // 检查用户权限 - if (!userInfo.access_level || userInfo.access_level < 2) { + if (!accessLevel || accessLevel < 2) { return res.status(403).json({ success: false, message: '权限不足,需要开发者及以上权限' }); } - const result = addAnswer(questionId, content, userInfo.username); + const result = addAnswer(questionId, content, username); res.status(201).json(result); } catch (error) { console.error('添加回答失败:', error); @@ -62,13 +74,13 @@ router.post('/questions/:questionId/answers', (req, res) => { }); // 删除问题 -router.delete('/questions/:questionId', (req, res) => { +router.delete('/questions/:questionId', authMiddleware, (req, res) => { try { const { questionId } = req.params; - const userInfo = req.body.userInfo || {}; + const accessLevel = req.session.user.access_level; // 检查用户权限 - if (!userInfo.access_level || userInfo.access_level < 3) { + if (!accessLevel || accessLevel < 3) { return res.status(403).json({ success: false, message: '权限不足,需要组长及以上权限' }); } @@ -81,13 +93,13 @@ router.delete('/questions/:questionId', (req, res) => { }); // 删除回答 -router.delete('/answers/:answerId', (req, res) => { +router.delete('/answers/:answerId', authMiddleware, (req, res) => { try { const { answerId } = req.params; - const userInfo = req.body.userInfo || {}; + const accessLevel = req.session.user.access_level; // 检查用户权限 - if (!userInfo.access_level || userInfo.access_level < 3) { + if (!accessLevel || accessLevel < 3) { return res.status(403).json({ success: false, message: '权限不足,需要组长及以上权限' }); } diff --git a/XNSimHtml/server.js b/XNSimHtml/server.js index 076ef15..8ea8ba6 100644 --- a/XNSimHtml/server.js +++ b/XNSimHtml/server.js @@ -4,6 +4,7 @@ const bodyParser = require('body-parser'); const os = require('os'); const cookieParser = require('cookie-parser'); const cors = require('cors'); +const session = require('express-session'); // 导入自定义模块 const { performCleanup } = require('./utils/xnCoreService'); @@ -45,6 +46,17 @@ app.use(cors({ credentials: true // 允许跨域请求携带cookie })); app.use(cookieParser()); +app.use(session({ + secret: 'xnsim-secret-key', // 用于签名 session ID cookie 的密钥 + resave: false, // 不强制保存 session + saveUninitialized: false, // 不强制将未初始化的 session 存储 + cookie: { + httpOnly: true, // 防止客户端 JavaScript 访问 cookie + secure: process.env.NODE_ENV === 'production', // 在生产环境使用 HTTPS + sameSite: 'strict', // 防止 CSRF 攻击 + maxAge: 30 * 60 * 1000 // cookie 有效期 30 分钟 + } +})); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); diff --git a/XNSimHtml/utils/xnCoreService.js b/XNSimHtml/utils/xnCoreService.js index c444f3c..ea4d3b5 100644 --- a/XNSimHtml/utils/xnCoreService.js +++ b/XNSimHtml/utils/xnCoreService.js @@ -30,10 +30,10 @@ let monitorLib; try { loginLib = ffi.Library(loginLibPath, { - 'validateUser': ['int', [BufferType, 'size_t', BufferType, 'size_t']], - 'getUserInfo': [StringType, ['int']], + 'validateUser': ['int', [StringType, 'int', StringType, 'int']], + 'getUserInfo': ['int', ['int', StringType, 'int']], 'cleanup': ['void', []], - 'registerUser': ['int', [BufferType, 'size_t', BufferType, 'size_t', BufferType, 'size_t']] + 'registerUser': ['int', [StringType, 'int', StringType, 'int', StringType, 'int']] }); } catch (error) { console.error(`加载 ${loginLibName} 失败:`, error);