You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Electron.js中安全调用SQL查询局域网非本地数据库?

问题描述

我正在开发一个需查询SQL数据库的系统,该数据库位于局域网内但非本地主机(非SQLExpress)。已实现从网页获取用户输入并将信息发送至Main.js,但不确定查询数据库的最优方式。对SQL和Electron.js均不熟悉,遇到以下问题:

  1. 参考Stack Overflow方案编写的代码中,dbFunctions不存在get方法,不清楚所需的SQL模块
  2. 不知道如何根据用户输入的账号密码修改数据库配置
  3. 硬编码登录逻辑可运行,但数据库查询部分无法正常工作
解决方案

1. 修正依赖与模块选择

你当前引入的sql模块不是正确的SQL Server操作库,代码里已经引入了tedious(SQL Server官方推荐的Node.js操作库),直接基于它实现数据库操作即可,无需额外的sql模块。

先确保安装依赖:

npm install tedious

2. 重构数据库连接与登录验证逻辑

步骤1:修改main.js的核心逻辑

将固定数据库配置改为可动态传入用户凭证的方式,同时实现安全的参数化查询(防止SQL注入):

const { app, BrowserWindow, ipcMain } = require('electron');
const { Connection, Request, TYPES } = require('tedious');
const path = require('path');
let mainWindow;

// 数据库基础配置(不含用户凭证)
const baseDbConfig = {
    server: '局域网数据库IP或主机名',  // 替换为你的实际地址
    authentication: {
        type: 'default',
        options: {}  // 留空,后续填充用户输入的账号密码
    },
    options: {
        encrypt: false,  // 局域网环境通常无需加密,按需调整
        database: '目标数据库名称',  // 替换为你的数据库名
        trustServerCertificate: true  // 局域网内可开启,避免证书验证问题
    }
};

function createWindow() {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,  // 关闭node集成,提升安全性
            contextIsolation: true,
            preload: path.join(__dirname, 'preload.js')
        },
    });

    mainWindow.loadFile(path.join(__dirname, 'index.html'));

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

app.on('ready', () => {
    createWindow();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// 处理登录IPC请求
ipcMain.on('login', (event, loginData) => {
    const { username, password } = loginData;
    console.log(`收到登录请求:用户名=${username}`);

    // 动态生成带用户凭证的数据库配置
    const dbConfig = {
        ...baseDbConfig,
        authentication: {
            ...baseDbConfig.authentication,
            options: {
                userName: username,
                password: password
            }
        }
    };

    // 创建数据库连接
    const connection = new Connection(dbConfig);

    connection.on('connect', (err) => {
        if (err) {
            console.error('数据库连接失败:', err.message);
            event.reply('login-status', false);
            return;
        }

        // 参数化查询,避免SQL注入
        const query = "SELECT COUNT(*) AS count FROM 你的用户表名 WHERE username = @username AND password = @password";
        const request = new Request(query, (err) => {
            if (err) {
                console.error('查询失败:', err.message);
                event.reply('login-status', false);
            }
            connection.close();
        });

        // 添加查询参数
        request.addParameter('username', TYPES.NVarChar, username);
        request.addParameter('password', TYPES.NVarChar, password);

        // 处理查询结果
        let loginSuccess = false;
        request.on('row', (columns) => {
            columns.forEach(column => {
                if (column.value > 0) loginSuccess = true;
            });
        });

        // 查询完成后返回结果
        request.on('requestCompleted', () => {
            event.reply('login-status', loginSuccess);
        });

        connection.execSql(request);
    });

    // 连接错误处理
    connection.on('error', (err) => {
        console.error('数据库连接错误:', err.message);
        event.reply('login-status', false);
    });
});

步骤2:修正preload.js的安全暴露逻辑

Electron中数据库操作必须在主进程完成,渲染进程仅能通过IPC通信调用,简化preload代码:

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('ElectronAPI', {
    // 发送登录请求到主进程
    login: (credentials) => ipcRenderer.send('login', credentials),
    // 监听登录状态返回
    onLoginStatus: (callback) => ipcRenderer.on('login-status', (_, isSuccess) => callback(isSuccess))
});

步骤3:更新renderer.js的IPC调用

使用preload暴露的安全API,避免直接操作ipcRenderer:

const loginForm = document.getElementById('login-form');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');

loginForm.addEventListener('submit', (event) => {
    event.preventDefault();

    const username = usernameInput.value.trim();
    const password = passwordInput.value.trim();

    if (!username || !password) {
        alert('请输入用户名和密码');
        return;
    }

    // 调用preload暴露的API发送登录请求
    window.ElectronAPI.login({ username, password });
});

// 监听登录状态结果
window.ElectronAPI.onLoginStatus((isSuccess) => {
    const messageElement = document.createElement('p');
    if (isSuccess) {
        messageElement.textContent = '登录成功';
        messageElement.style.color = 'green';
    } else {
        messageElement.textContent = '用户名或密码错误';
        messageElement.style.color = 'red';
    }
    loginForm.appendChild(messageElement);
    setTimeout(() => {
        loginForm.removeChild(messageElement);
    }, 2000);
});

3. 关键注意事项

  • 安全规范:永远不要在渲染进程直接操作数据库,必须通过主进程IPC通信完成;强制使用参数化查询,杜绝SQL注入风险。
  • 局域网配置:确保Electron所在设备能访问局域网数据库服务器,检查防火墙、数据库端口(默认1433)是否开放。
  • 密码存储:生产环境中禁止明文存储密码,建议数据库存储密码哈希值(如SHA256),登录时对比哈希值而非明文。

内容的提问来源于stack exchange,提问作者addyHyena

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:52:37