V0.23.1.250617_alpha:修改了登陆及用户信息相关的内容
This commit is contained in:
parent
de60d87f01
commit
ae419e5b83
304
Login/login.cpp
304
Login/login.cpp
@ -11,17 +11,20 @@
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
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<int>(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<const char*>(username_buffer), username_length);
|
||||
std::string password_str(static_cast<const char*>(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<const char*>(sqlite3_column_text(stmt, 1));
|
||||
userInfo["id"] = sqlite3_column_int(stmt, 0);
|
||||
userInfo["username"] = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
|
||||
userInfo["access_level"] = sqlite3_column_int(stmt, 3);
|
||||
userInfo["full_name"] = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
|
||||
userInfo["phone"] = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 5));
|
||||
userInfo["email"] = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 6));
|
||||
userInfo["department"] = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 7));
|
||||
userInfo["position"] = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 8));
|
||||
userInfo["full_name"] =
|
||||
reinterpret_cast<const char *>(sqlite3_column_text(stmt, 4));
|
||||
userInfo["phone"] = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 5));
|
||||
userInfo["email"] = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 6));
|
||||
userInfo["department"] =
|
||||
reinterpret_cast<const char *>(sqlite3_column_text(stmt, 7));
|
||||
userInfo["position"] = reinterpret_cast<const char *>(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<const unsigned char *>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
std::string connectionName =
|
||||
"usercheck_"
|
||||
+ std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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<const char *>(username_buffer);
|
||||
const char *password_data = static_cast<const char *>(password_buffer);
|
||||
const char *userinfo_data = static_cast<const char *>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
std::string connectionName =
|
||||
"userreg_"
|
||||
+ std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 5, userInfo["phone"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 6, userInfo["email"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 7, userInfo["department"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 8, userInfo["position"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 4, userInfo["full_name"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 5, userInfo["phone"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 6, userInfo["email"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 7, userInfo["department"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 8, userInfo["position"].get<std::string>().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<const char *>(old_password_buffer);
|
||||
const char *new_password_data = static_cast<const char *>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
std::string connectionName =
|
||||
"changepwd_"
|
||||
+ std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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<const char*>(sqlite3_column_text(stmt, 0));
|
||||
std::string storedPassword = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||
std::string username =
|
||||
reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
|
||||
std::string storedPassword =
|
||||
reinterpret_cast<const char *>(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<const char *>(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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
std::string connectionName =
|
||||
"userupdate_"
|
||||
+ std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, userInfo["phone"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, userInfo["email"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 4, userInfo["department"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 5, userInfo["position"].get<std::string>().c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 1, userInfo["full_name"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, userInfo["phone"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, userInfo["email"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 4, userInfo["department"].get<std::string>().c_str(), -1,
|
||||
SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 5, userInfo["position"].get<std::string>().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::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
std::string connectionName =
|
||||
"useraccess_"
|
||||
+ std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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);
|
||||
|
Binary file not shown.
@ -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();
|
||||
|
@ -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');
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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: '未登录'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -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: '权限不足,需要组长及以上权限' });
|
||||
}
|
||||
|
||||
|
@ -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 }));
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user