用脚本实现自动密码输入器

wen 实用脚本 3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">自动密码输入器 · 安全工具</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
        }
        body {
            background: #f0f3f8;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            padding: 20px;
        }
        .card {
            background: #ffffff;
            border-radius: 28px;
            box-shadow: 0 20px 40px -12px rgba(0, 20, 40, 0.25);
            max-width: 600px;
            width: 100%;
            padding: 30px 28px;
            transition: box-shadow 0.2s;
        }
        .card:hover {
            box-shadow: 0 24px 48px -12px rgba(0, 20, 40, 0.35);
        }
        h1 {
            font-size: 1.9rem;
            font-weight: 600;
            letter-spacing: -0.5px;
            color: #0b1a2f;
            display: flex;
            align-items: center;
            gap: 12px;
            margin-top: 0;
            margin-bottom: 0.25rem;
        }
        .subhead {
            color: #4a5b6e;
            font-size: 0.95rem;
            margin-bottom: 1.8rem;
            border-left: 4px solid #2b6fde;
            padding-left: 14px;
            background: #f6f9fc;
            border-radius: 0 12px 12px 0;
            line-height: 1.4;
        }
        .settings {
            background: #f8fafd;
            border-radius: 20px;
            padding: 18px 18px 12px;
            margin-bottom: 24px;
        }
        .label-flex {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 6px;
        }
        .label-flex label {
            font-weight: 500;
            color: #1a2c40;
            font-size: 0.9rem;
        }
        .badge {
            background: #dee6f0;
            color: #1a2c40;
            font-size: 0.7rem;
            font-weight: 500;
            padding: 2px 10px;
            border-radius: 30px;
            letter-spacing: 0.2px;
        }
        input[type="password"], input[type="text"] {
            width: 100%;
            padding: 12px 16px;
            border: 1.5px solid #d9e1ec;
            border-radius: 60px;
            font-size: 1rem;
            background: white;
            transition: 0.15s;
            outline: none;
        }
        input[type="password"]:focus, input[type="text"]:focus {
            border-color: #2b6fde;
            box-shadow: 0 0 0 3px rgba(43, 111, 222, 0.2);
        }
        .input-group {
            margin-bottom: 18px;
            position: relative;
        }
        .reveal-btn {
            position: absolute;
            right: 6px;
            top: 50%;
            transform: translateY(-50%);
            background: transparent;
            border: none;
            font-size: 0.9rem;
            color: #2b6fde;
            font-weight: 500;
            cursor: pointer;
            padding: 8px 14px;
            border-radius: 40px;
            transition: background 0.1s;
        }
        .reveal-btn:hover {
            background: #eaf0fa;
        }
        .hotkey-row {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 14px;
            margin: 10px 0 6px;
        }
        .hotkey-box {
            display: flex;
            align-items: center;
            gap: 6px;
            background: #edf2f9;
            padding: 4px 14px 4px 10px;
            border-radius: 40px;
            font-size: 0.85rem;
            font-weight: 500;
            color: #1a2c40;
        }
        kbd {
            background: #ffffff;
            border: 1px solid #bcc9db;
            box-shadow: 0 1px 2px rgba(0,0,0,0.06);
            padding: 0 8px;
            line-height: 1.8;
            border-radius: 6px;
            font-size: 0.8rem;
            font-weight: 600;
            color: #0a1a2a;
            display: inline-block;
        }
        .action-bar {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
            align-items: center;
            gap: 12px;
            margin: 18px 0 12px;
        }
        .btn {
            background: white;
            border: 1.5px solid #d0dbe9;
            padding: 10px 22px;
            border-radius: 60px;
            font-weight: 500;
            font-size: 0.9rem;
            color: #1a2c40;
            cursor: pointer;
            transition: all 0.12s;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.02);
        }
        .btn-primary {
            background: #1a2c40;
            border-color: #1a2c40;
            color: white;
            box-shadow: 0 4px 10px rgba(26, 44, 64, 0.15);
        }
        .btn-primary:hover {
            background: #2b4058;
            transform: scale(0.97);
        }
        .btn-primary:active {
            background: #0f1f30;
            transform: scale(0.95);
        }
        .btn-outline {
            background: transparent;
            border-color: #b9c9dd;
        }
        .btn-outline:hover {
            background: #eef4fc;
            border-color: #8da3c2;
        }
        .btn-success {
            background: #1f8b4c;
            border-color: #1f8b4c;
            color: white;
        }
        .btn-success:hover {
            background: #16703b;
            transform: scale(0.97);
        }
        .status {
            padding: 12px 16px;
            background: #f2f6fe;
            border-radius: 60px;
            font-size: 0.9rem;
            color: #1a2c40;
            margin: 12px 0 0;
            display: flex;
            align-items: center;
            gap: 8px;
            border: 1px solid #dfe9f5;
            transition: 0.15s;
        }
        .status .icon {
            font-size: 1.2rem;
        }
        .status.success {
            background: #e2f3e9;
            border-color: #a7d8b9;
        }
        .status.waiting {
            background: #fff4dd;
            border-color: #fadc9c;
        }
        .footer-note {
            font-size: 0.75rem;
            color: #6f829a;
            text-align: center;
            margin-top: 20px;
            border-top: 1px solid #e6edf6;
            padding-top: 16px;
        }
        .footer-note a {
            color: #1a2c40;
            text-decoration: none;
            font-weight: 500;
        }
        .flex-grow {
            flex: 1;
        }
        @media (max-width: 480px) {
            .card { padding: 20px 16px; }
            .action-bar { flex-direction: column; align-items: stretch; }
            .btn { justify-content: center; }
        }
    </style>
</head>
<body>
<div class="card" role="main">
    <h1>
        <span>🔐</span> 自动密码输入器
    </h1>
    <div class="subhead">
        安全保存密码 · 一键填入任何输入框<br>
        <span style="font-size:0.85rem; opacity:0.8;">全局快捷键 <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (可自定义)</span>
    </div>
    <!-- 密码存储区 -->
    <div class="settings">
        <div class="label-flex">
            <label for="masterPassword">🔑 主密码 (加密存储)</label>
            <span class="badge">仅本地</span>
        </div>
        <div class="input-group">
            <input type="password" id="masterPassword" placeholder="输入要保存的密码..." autocomplete="off">
            <button class="reveal-btn" id="toggleMasterPass" aria-label="显示/隐藏密码">显示</button>
        </div>
        <div class="label-flex" style="margin-top:12px;">
            <label for="hotkeyInput">⌨️ 快捷键设置</label>
            <span class="badge">全局</span>
        </div>
        <div class="input-group" style="margin-bottom:4px;">
            <input type="text" id="hotkeyInput" value="Ctrl+Shift+P" placeholder="Ctrl+Shift+P">
        </div>
        <div class="hotkey-row">
            <span class="hotkey-box">🖱️ 点击填入</span>
            <span class="hotkey-box"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd></span>
        </div>
    </div>
    <!-- 操作栏 -->
    <div class="action-bar">
        <button class="btn btn-primary" id="savePasswordBtn">💾 保存密码</button>
        <button class="btn btn-outline" id="clearPasswordBtn">🗑️ 清除</button>
        <button class="btn btn-success" id="pastePasswordBtn">📋 填入当前输入框</button>
    </div>
    <!-- 状态 / 反馈 -->
    <div class="status waiting" id="statusIndicator">
        <span class="icon">⏳</span>
        <span id="statusMessage">等待保存密码 …</span>
    </div>
    <!-- 模拟测试输入框 (演示) -->
    <div style="margin: 28px 0 6px; background:#f6fafe; border-radius: 60px; padding: 8px 8px 8px 20px; display: flex; align-items: center; border:1px solid #e2ecf9;">
        <span style="color:#2b4d72; font-size:0.9rem;">🧪 测试焦点输入框:</span>
        <input type="text" id="demoInput" placeholder="点击这里, 然后按快捷键" style="border:none; background:transparent; flex:1; padding:8px 8px; outline:none; font-size:0.95rem;">
    </div>
    <div class="footer-note">
        ⚡ 密码仅保存在浏览器内存中 · 关闭页面自动清除 ·
        <a href="#" id="lockBtn" style="cursor:pointer;">🔒 手动锁定</a>
    </div>
</div>
<script>
    (function() {
        "use strict";
        // DOM 元素
        const masterPassInput = document.getElementById('masterPassword');
        const hotkeyInput = document.getElementById('hotkeyInput');
        const saveBtn = document.getElementById('savePasswordBtn');
        const clearBtn = document.getElementById('clearPasswordBtn');
        const pasteBtn = document.getElementById('pastePasswordBtn');
        const statusIndicator = document.getElementById('statusIndicator');
        const statusMessage = document.getElementById('statusMessage');
        const togglePassBtn = document.getElementById('toggleMasterPass');
        const demoInput = document.getElementById('demoInput');
        const lockBtn = document.getElementById('lockBtn');
        // ----- 状态 -----
        let savedPassword = '';               // 内存中保存的明文密码
        let isLocked = false;                // 手动锁定 (不可填入)
        let currentPassword = '';            // 用于显示/隐藏
        // ----- 辅助函数 更新状态UI -----
        function updateStatus(type, msg) {
            statusIndicator.className = 'status';
            const iconSpan = statusIndicator.querySelector('.icon');
            if (type === 'success') {
                statusIndicator.classList.add('success');
                iconSpan.textContent = '✅';
            } else if (type === 'waiting') {
                statusIndicator.classList.add('waiting');
                iconSpan.textContent = '⏳';
            } else if (type === 'error') {
                statusIndicator.style.background = '#fee9e7';
                statusIndicator.style.borderColor = '#f5b8b1';
                iconSpan.textContent = '❌';
            } else if (type === 'info') {
                statusIndicator.style.background = '#e8f0fe';
                statusIndicator.style.borderColor = '#b6cceb';
                iconSpan.textContent = 'ℹ️';
            }
            statusMessage.textContent = msg;
            // 重置额外样式 (error时)
            if (type !== 'error') {
                statusIndicator.style.background = '';
                statusIndicator.style.borderColor = '';
            }
        }
        // 刷新: 显示当前是否保存密码
        function refreshStatus() {
            if (isLocked) {
                updateStatus('info', '🔒 已手动锁定,无法填入密码');
                return;
            }
            if (savedPassword && savedPassword.length > 0) {
                const masked = savedPassword.substring(0, 2) + '••••' + (savedPassword.length > 4 ? savedPassword.slice(-2) : '');
                updateStatus('success', `密码已保存 (${masked}) · 就绪`);
            } else {
                updateStatus('waiting', '尚未保存密码 · 请先输入并保存');
            }
        }
        // ----- 核心: 填入密码到当前聚焦输入框 -----
        function fillPasswordToActiveElement() {
            // 锁定或密码为空
            if (isLocked) {
                updateStatus('error', '🔒 已锁定,请先解锁 (点击 "手动锁定" 解除)');
                return false;
            }
            if (!savedPassword || savedPassword.length === 0) {
                updateStatus('error', '❌ 没有已保存的密码,请先保存');
                return false;
            }
            const activeEl = document.activeElement;
            // 必须是一个输入框 (input 或 textarea)
            if (!activeEl) {
                updateStatus('error', '❌ 没有焦点元素');
                return false;
            }
            const tag = activeEl.tagName.toLowerCase();
            const validInput = (tag === 'input' || tag === 'textarea');
            // 如果是 input 但 type 不是 text/search/email/password/tel/url 等可输入的排除? 但密码框也是password, 允许
            const inputType = (activeEl.type || '').toLowerCase();
            const allowedTypes = ['text', 'password', 'search', 'email', 'tel', 'url', 'number', 'textarea', ''];
            if (!validInput || (tag === 'input' && !allowedTypes.includes(inputType) && inputType !== 'password')) {
                updateStatus('error', '❌ 焦点不在可输入框上 (点击输入框再试)');
                return false;
            }
            // 使用 setValue 模拟输入 (触发 input/change 事件)
            const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
                window.HTMLInputElement.prototype, 'value'
            )?.set || Object.getOwnPropertyDescriptor(
                window.HTMLTextAreaElement.prototype, 'value'
            )?.set;
            if (nativeInputValueSetter) {
                nativeInputValueSetter.call(activeEl, savedPassword);
            } else {
                // fallback
                activeEl.value = savedPassword;
            }
            // 触发必要的事件 (让站点/框架检测到)
            activeEl.dispatchEvent(new Event('input', { bubbles: true }));
            activeEl.dispatchEvent(new Event('change', { bubbles: true }));
            // 如果焦点在demoInput上,做特殊高亮
            if (activeEl === demoInput) {
                demoInput.style.background = '#e2f0e0';
                setTimeout(() => { demoInput.style.background = ''; }, 300);
            }
            updateStatus('success', `✅ 密码已填入 “${activeEl.name || activeEl.id || activeEl.placeholder || '当前输入框'}”`);
            return true;
        }
        // ----- 保存密码 -----
        function savePassword() {
            const pass = masterPassInput.value;
            if (!pass || pass.trim() === '') {
                updateStatus('error', '❌ 密码不能为空');
                return;
            }
            savedPassword = pass;
            // 清除手动锁定
            if (isLocked) {
                isLocked = false;
            }
            refreshStatus();
            // 清空输入框 (安全)
            masterPassInput.value = '';
            updateStatus('success', '🔐 密码已安全保存 (仅内存)');
        }
        // ----- 清除密码 -----
        function clearPassword() {
            if (savedPassword === '' && !isLocked) {
                updateStatus('info', 'ℹ️ 没有已保存的密码');
                return;
            }
            savedPassword = '';
            isLocked = false;
            masterPassInput.value = '';
            refreshStatus();
            updateStatus('waiting', '🗑️ 密码已清除');
        }
        // ----- 锁定/解锁切换 -----
        function toggleLock() {
            if (isLocked) {
                isLocked = false;
                lockBtn.innerHTML = '🔒 手动锁定';
                refreshStatus();
            } else {
                isLocked = true;
                lockBtn.innerHTML = '🔓 解除锁定';
                updateStatus('info', '🔒 已锁定,无法填入密码 (点击解除)');
            }
        }
        // ----- 显示/隐藏主密码输入框 -----
        let passVisible = false;
        togglePassBtn.addEventListener('click', function() {
            if (!passVisible) {
                masterPassInput.type = 'text';
                togglePassBtn.textContent = '隐藏';
                passVisible = true;
            } else {
                masterPassInput.type = 'password';
                togglePassBtn.textContent = '显示';
                passVisible = false;
            }
        });
        // ----- 快捷键解析与注册 -----
        let currentHotkey = 'Ctrl+Shift+P';   // 默认
        let registeredCallback = null;
        function parseHotkey(hotkeyStr) {
            const parts = hotkeyStr.split('+').map(s => s.trim().toLowerCase());
            const keys = {
                ctrl: false,
                shift: false,
                alt: false,
                meta: false,
                key: ''
            };
            for (const part of parts) {
                if (part === 'ctrl' || part === 'control') keys.ctrl = true;
                else if (part === 'shift') keys.shift = true;
                else if (part === 'alt') keys.alt = true;
                else if (part === 'meta' || part === 'win' || part === 'cmd') keys.meta = true;
                else if (part.length === 1) keys.key = part.toUpperCase();
                else if (part.length > 1) keys.key = part.charAt(0).toUpperCase() + part.slice(1); // e.g. "P"
                else keys.key = part.toUpperCase();
            }
            // 如果key为空, 默认 'P'
            if (!keys.key) keys.key = 'P';
            return keys;
        }
        function registerHotkey(hotkeyStr) {
            // 移除旧监听 (通过保存的handler)
            if (registeredCallback) {
                document.removeEventListener('keydown', registeredCallback);
                registeredCallback = null;
            }
            const keys = parseHotkey(hotkeyStr);
            const handler = function(e) {
                // 检查修饰键
                const ctrlOK = keys.ctrl ? (e.ctrlKey || e.metaKey) : true; // 如果要求ctrl, 则必须按下ctrl或meta
                const shiftOK = keys.shift ? e.shiftKey : true;
                const altOK = keys.alt ? e.altKey : true;
                // 特例: 如果用户设置单纯 "P" 无修饰, 则避免与其他输入冲突, 但保留
                const metaOK = keys.meta ? (e.metaKey || e.ctrlKey) : true; // 宽松
                // 排除输入框内输入字符 (如果无修饰, 不在输入框触发)
                if (!keys.ctrl && !keys.shift && !keys.alt && !keys.meta) {
                    // 无修饰键: 只在非输入框中生效? 但密码填入需要聚焦输入框, 造成无限循环? 
                    // 避免: 如果无修饰且焦点在输入框, 不触发快捷键 (防止输入字符)
                    const tag = (e.target.tagName || '').toLowerCase();
                    if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) {
                        return;
                    }
                }
                // 检查key
                const pressedKey = e.key.toUpperCase();
                if (pressedKey !== keys.key.toUpperCase()) return;
                // 修饰键组合检查
                if (keys.ctrl && !e.ctrlKey && !e.metaKey) return;
                if (keys.shift && !e.shiftKey) return;
                if (keys.alt && !e.altKey) return;
                if (keys.meta && !e.metaKey && !e.ctrlKey) return;
                // 如果无修饰键但上面已经过滤了输入框, 则这里可以执行
                // 额外: 如果只按了一个字母且没有修饰, 并且在输入框中, 已经return, 安全。
                // 防止默认行为 (如ctrl+shift+P可能打开打印)
                e.preventDefault();
                e.stopPropagation();
                // 执行填入
                fillPasswordToActiveElement();
            };
            document.addEventListener('keydown', handler);
            registeredCallback = handler;
            currentHotkey = hotkeyStr;
        }
        // ----- 更新快捷键设置 -----
        function applyHotkeyFromInput() {
            let raw = hotkeyInput.value.trim();
            if (!raw || raw.length < 2) {
                raw = 'Ctrl+Shift+P';
                hotkeyInput.value = raw;
            }
            // 简单格式化: 首字母大写等
            registerHotkey(raw);
            updateStatus('info', `⌨️ 快捷键已更新: ${raw}`);
            // 刷新状态
            setTimeout(refreshStatus, 100);
        }
        // ----- 初始化 -----
        function init() {
            // 默认注册
            registerHotkey('Ctrl+Shift+P');
            // 如果有保存过? 无
            refreshStatus();
            // 事件绑定
            saveBtn.addEventListener('click', savePassword);
            clearBtn.addEventListener('click', clearPassword);
            pasteBtn.addEventListener('click', function() {
                fillPasswordToActiveElement();
            });
            lockBtn.addEventListener('click', function(e) {
                e.preventDefault();
                toggleLock();
            });
            // 快捷键修改
            hotkeyInput.addEventListener('change', applyHotkeyFromInput);
            hotkeyInput.addEventListener('blur', function() {
                // 如果为空恢复默认
                if (!hotkeyInput.value.trim()) {
                    hotkeyInput.value = 'Ctrl+Shift+P';
                    applyHotkeyFromInput();
                }
            });
            // 回车保存密码 (在密码输入框)
            masterPassInput.addEventListener('keydown', function(e) {
                if (e.key === 'Enter') {
                    e.preventDefault();
                    savePassword();
                }
            });
            // demo输入框演示提示
            demoInput.addEventListener('focus', function() {
                if (savedPassword) {
                    updateStatus('info', '💡 现在按快捷键 (或点"填入") 自动填充密码');
                } else {
                    updateStatus('waiting', '💡 先保存密码,再按快捷键填充');
                }
            });
            // 额外: 如果用户在输入框输入快捷键, 友好的示例
            console.log('🔐 自动密码输入器已启动 | 快捷键填充');
        }
        init();
    })();
</script>
</body>
</html>

核心功能与操作路径

您可以通过以下方式快速使用这个工具,它旨在简化重复输入密码的流程。

用脚本实现自动密码输入器

  1. 保存密码:在“主密码”输入框中输入您的密码,点击“保存密码”按钮即可安全地存储在浏览器内存中。
  2. 填入密码:点击任何需要输入密码的输入框使其获得焦点,然后按下您设置的快捷键(默认 Ctrl+Shift+P),密码会自动填入。
  3. 管理与安全:您可以随时清除已保存的密码或点击“手动锁定”来防止误填入,状态栏会清晰显示当前密码的保存状态和操作反馈。

抱歉,评论功能暂时关闭!