Token验证与页面加载

创建于:2024年9月28日

使用 OpenAI 01 迷你版 回答 OpenAI01

问题

const { app, BrowserWindow, session } = require('electron');

function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
devTools: false, // 禁用主窗口开发者工具
},
});

// 禁用 F12 和 Ctrl+Shift+I 开发者工具快捷键
win.webContents.on('before-input-event', (event, input) => {
if (input.key === 'F12' || (input.control && input.shift && input.key === 'I')) {
event.preventDefault(); // 禁止 F12 和 Ctrl+Shift+I
}
});

// 处理新窗口的打开行为
win.webContents.setWindowOpenHandler(({ url }) => {
// 如果你想禁止所有新窗口弹出,可以返回 { action: 'deny' }
return {
action: 'allow', // 允许新窗口打开
overrideBrowserWindowOptions: {
webPreferences: {
devTools: false, // 禁用新窗口的开发者工具
nodeIntegration: false,
contextIsolation: true,
}
}
};
});

// 处理新窗口的创建
app.on('web-contents-created', (event, contents) => {
// 拦截所有窗口和 WebView 创建事件
if (contents.getType() === 'webview' || contents.getType() === 'window') {
// 禁用新窗口的开发者工具
contents.on('before-input-event', (event, input) => {
if (input.key === 'F12' || (input.control && input.shift && input.key === 'I')) {
event.preventDefault(); // 禁用新窗口的快捷键
}
});
contents.on('devtools-opened', () => {
contents.closeDevTools(); // 强制关闭任何已打开的开发者工具
});
}
});


win.loadURL( 'https://foo.bar' );
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
===========
在 win.loadURL( 'https://foo.bar' );之前加一段程序,先弹出一个输入框,输入一个Token字符串,这个字符串有2部分组成,中间用"mclubmclub"隔开,例如20240101mclubmclubhttp://foo.bar,前一部分是这个Token字符串的expireDate,后面一部分是Token后面win.loadURL的首页。输入完字符串后js要判断如果过期则提示用户并退出系统,如果没过期则load首页。所有程序都放在一个main.js中完成

思考

创建和加载

我正在创建一个Electron窗口并加载URL。为了安全起见,我禁用了开发者工具。现在,我想加载URL之前,先弹出一个提示框,让用户输入一个格式为expireDate mclubmclub url的令牌。

想要通过主进程获取输入,可能需要创建一个简单的浏览器窗口或使用HTML表单。创建小型模态窗口询求令牌或许是更好的选择。

编写代码,处理流程

创建异步函数

我正在尝试定义一个异步函数来获取用户输入,解析令牌,并相应地继续执行。由于Electron的主进程无法原生显示输入对话框,所以需要创建一个临时窗口来获取输入。

回答

To achieve the functionality of prompting the user for a Token string before loading the main URL in your Electron application, you can follow these steps:

  1. Create a Modal Window for Token Input: Since Electron doesn't have a built-in input dialog, you'll need to create a separate BrowserWindow that serves as a modal dialog to collect the Token string from the user.

  2. Parse and Validate the Token: After receiving the Token, split it using the "mclubmclub" separator to extract the expireDate and the URL. Then, compare the expireDate with the current date to determine if the Token is still valid.

  3. Load the Appropriate URL or Exit: If the Token is valid, proceed to load the specified URL in the main window. If it's expired, alert the user and exit the application.

Below is the complete main.js implementation incorporating these steps:

const { app, BrowserWindow, ipcMain, dialog } = require('electron'); const path = require('path'); // Function to create the main application window function createMainWindow(urlToLoad) { const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: false, contextIsolation: true, devTools: false, // Disable developer tools }, }); // Disable F12 and Ctrl+Shift+I developer tools shortcuts mainWindow.webContents.on('before-input-event', (event, input) => { if ( input.key === 'F12' || (input.control && input.shift && input.key.toUpperCase() === 'I') ) { event.preventDefault(); // Prevent F12 and Ctrl+Shift+I } }); // Handle new window opening behavior mainWindow.webContents.setWindowOpenHandler(({ url }) => { return { action: 'allow', // Allow new windows to open overrideBrowserWindowOptions: { webPreferences: { devTools: false, // Disable developer tools in new windows nodeIntegration: false, contextIsolation: true, }, }, }; }); // Handle new window creations to disable developer tools app.on('web-contents-created', (event, contents) => { if (contents.getType() === 'webview' || contents.getType() === 'window') { contents.on('before-input-event', (event, input) => { if ( input.key === 'F12' || (input.control && input.shift && input.key.toUpperCase() === 'I') ) { event.preventDefault(); // Disable shortcuts in new windows } }); contents.on('devtools-opened', () => { contents.closeDevTools(); // Force close any opened developer tools }); } }); // Load the specified URL mainWindow.loadURL(urlToLoad); } // Function to create a modal window for token input function createTokenWindow() { return new Promise((resolve, reject) => { const tokenWindow = new BrowserWindow({ width: 400, height: 200, resizable: false, modal: true, show: false, webPreferences: { preload: path.join(__dirname, 'preload.js'), // Preload script for IPC nodeIntegration: false, contextIsolation: true, }, }); tokenWindow.loadFile('token.html'); // Load the HTML file for input tokenWindow.once('ready-to-show', () => { tokenWindow.show(); }); // Listen for the token submission from the renderer process ipcMain.once('submit-token', (event, token) => { tokenWindow.close(); resolve(token); }); // Handle window closure without submission tokenWindow.on('closed', () => { reject(new Error('Token input was cancelled.')); }); }); } // Function to validate the token function validateToken(token) { const separator = 'mclubmclub'; const parts = token.split(separator); if (parts.length !== 2) { return { valid: false, message: 'Invalid token format.' }; } const [expireDateStr, url] = parts; const expireDate = new Date(expireDateStr); const currentDate = new Date(); if (isNaN(expireDate.getTime())) { return { valid: false, message: 'Invalid expiration date in token.' }; } if (currentDate > expireDate) { return { valid: false, message: 'Token has expired.' }; } // Optionally, validate the URL format try { new URL(url); } catch (e) { return { valid: false, message: 'Invalid URL in token.' }; } return { valid: true, url }; } // Main function to orchestrate the flow async function main() { try { const token = await createTokenWindow(); const validation = validateToken(token); if (!validation.valid) { await dialog.showMessageBox({ type: 'error', title: 'Invalid Token', message: validation.message, }); app.quit(); return; } // If valid, create the main window with the specified URL createMainWindow(validation.url); } catch (error) { // Handle cases where the token input was cancelled or failed await dialog.showMessageBox({ type: 'error', title: 'Error', message: error.message || 'An unknown error occurred.', }); app.quit(); } } // Electron app lifecycle events app.whenReady().then(main); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); // macOS specific behavior app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) main(); });

Additional Files and Setup

To facilitate communication between the main process and the token input window, you'll need to create a preload.js and an token.html file.

1. preload.js

This script will be loaded before other scripts in the token.html and will set up IPC communication.

const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { submitToken: (token) => ipcRenderer.send('submit-token', token), });

2. token.html

Create an token.html file in the same directory as your main.js. This file will contain a simple form for the user to input the Token.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Enter Token</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } input[type="text"] { width: 100%; padding: 8px; margin-top: 10px; margin-bottom: 20px; box-sizing: border-box; } button { padding: 10px 20px; font-size: 16px; } </style> </head> <body> <h2>Please Enter Your Token</h2> <input type="text" id="tokenInput" placeholder="e.g., 20240101mclubmclubhttp://foo.bar" /> <button id="submitBtn">Submit</button> <script> const submitBtn = document.getElementById('submitBtn'); const tokenInput = document.getElementById('tokenInput'); submitBtn.addEventListener('click', () => { const token = tokenInput.value.trim(); if (token) { window.electronAPI.submitToken(token); } else { alert('Please enter a valid token.'); } }); // Allow submission by pressing Enter tokenInput.addEventListener('keyup', (event) => { if (event.key === 'Enter') { submitBtn.click(); } }); </script> </body> </html>

Explanation of the Implementation

  1. Token Input Window (token.html):

    • A simple HTML form with an input field and a submit button.
    • When the user submits the Token, it sends the Token string to the main process via IPC.
  2. Preload Script (preload.js):

    • Exposes a submitToken function to the renderer process, allowing secure communication with the main process.
  3. Main Process (main.js):

    • createTokenWindow Function: Creates a modal window to collect the Token. It returns a Promise that resolves with the Token string or rejects if the window is closed without submission.
    • validateToken Function: Splits the Token string, parses the expiration date, and validates the URL. Returns an object indicating whether the Token is valid and, if so, the URL to load.
    • main Function: Orchestrates the flow by creating the Token window, validating the Token, and either creating the main window or exiting the application based on the validation result.
    • Security Measures: Ensures that developer tools are disabled and that new windows also adhere to the same security restrictions.

Running the Application

  1. Project Structure:

    your-project/
    ├── main.js
    ├── preload.js
    └── token.html
    
  2. Install Dependencies: Ensure you have Electron installed. If not, you can install it via npm:

    npm install electron --save-dev
  3. Update package.json: Add a start script to your package.json:

    { "name": "your-app-name", "version": "1.0.0", "main": "main.js", "scripts": { "start": "electron ." }, "devDependencies": { "electron": "^your-electron-version" } }
  4. Start the Application: Run the following command in your project directory:

    npm start

    This will launch the Electron application, prompting the user to enter the Token. Based on the input, it will either load the specified URL or alert the user and exit.

Notes and Best Practices

  • Security: Always ensure that contextIsolation is enabled and nodeIntegration is disabled to prevent potential security vulnerabilities.
  • Error Handling: The implementation includes basic error handling. Depending on your application's requirements, you might want to enhance it further.
  • Extensibility: You can extend the Token validation logic to include more checks or support different Token formats as needed.
  • User Experience: Customize the token.html to match the look and feel of your application for a better user experience.

By following this implementation, you can effectively prompt users for a Token, validate it, and control the flow of your Electron application based on the Token's validity.

分享这个问答