从零搭建安全可靠的密码管理器全攻略

Newb.蕴轩 安全 阅读 908
赞 48 收藏
二维码
手机扫码查看
反馈

先看效果,再看代码

最近在项目里捣鼓密码管理器,折腾了半天终于搞定了一个还算不错的实现。这个密码管理器可以用在一些需要用户输入和存储密码的场景,比如登录、注册等。亲测有效,下面直接上代码。

核心代码就这几行

首先,我们来看一下最核心的部分:如何生成一个随机的强密码。这里我用的是 JavaScript 的 crypto 模块来生成随机字符串。

function generateStrongPassword(length = 16) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+~|}{[]:;?><,./-=';
let password = '';
const crypto = window.crypto || window.msCrypto;

if (crypto && crypto.getRandomValues) {
const values = new Uint32Array(length);
crypto.getRandomValues(values);
for (let i = 0; i < length; i++) {
password += charset[values[i] % charset.length];
}
} else {
for (let i = 0; i < length; i++) {
password += charset[Math.floor(Math.random() * charset.length)];
}
}

return password;
}</code></pre>

<p>这段代码的核心就是生成一个指定长度的随机密码。charset 里包含了大小写字母、数字和一些特殊字符,确保生成的密码足够复杂。crypto.getRandomValues 是现代浏览器提供的一个方法,可以生成更安全的随机数。</p>

<h2>把这个密码存起来</h2>
<p>生成了密码之后,我们需要把它存起来。这里我用的是浏览器的
localStorage 来存储密码。当然,你也可以用其他方式,比如 sessionStorage 或者后端数据库。</p>

<pre class="pure-highlightjs line-numbers language-javascript"><code class="no-highlight language-javascript">function savePassword(username, password) {
const userPasswords = JSON.parse(localStorage.getItem(&#039;userPasswords&#039;)) || {};
userPasswords[username] = password;
localStorage.setItem(&#039;userPasswords&#039;, JSON.stringify(userPasswords));
}

function getPassword(username) {
const userPasswords = JSON.parse(localStorage.getItem(&#039;userPasswords&#039;)) || {};
return userPasswords[username] || null;
}</code></pre>

<p>这两个函数分别用于保存和获取用户的密码。savePassword 将用户名和密码以 JSON 格式存入 localStoragegetPassword 则从 localStorage 中读取密码。</p>

<h2>前端展示和交互</h2>
<p>接下来是前端部分,我们需要一个简单的表单来让用户输入用户名,并生成并显示密码。</p>

<pre class="pure-highlightjs line-numbers language-html"><code class="no-highlight language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;Password Manager&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Password Manager&lt;/h1&gt;
&lt;form id=&quot;passwordForm&quot;&gt;
&lt;label for=&quot;username&quot;&gt;Username:&lt;/label&gt;
&lt;input type=&quot;text&quot; id=&quot;username&quot; name=&quot;username&quot; required&gt;
&lt;br&gt;&lt;br&gt;
&lt;button type=&quot;button&quot; onclick=&quot;generateAndSavePassword()&quot;&gt;Generate and Save Password&lt;/button&gt;
&lt;br&gt;&lt;br&gt;
&lt;label for=&quot;generatedPassword&quot;&gt;Generated Password:&lt;/label&gt;
&lt;input type=&quot;text&quot; id=&quot;generatedPassword&quot; readonly&gt;
&lt;/form&gt;

&lt;script src=&quot;passwordManager.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>

<p>这是一个非常简单的 HTML 表单,用户输入用户名后点击按钮,会调用 generateAndSavePassword 函数生成并保存密码。</p>

<pre class="pure-highlightjs line-numbers language-javascript"><code class="no-highlight language-javascript">function generateAndSavePassword() {
const username = document.getElementById(&#039;username&#039;).value;
const password = generateStrongPassword();
savePassword(username, password);
document.getElementById(&#039;generatedPassword&#039;).value = password;
}</code></pre>

<h2>踩坑提醒:这三点一定注意</h2>
<p>在实际开发过程中,我踩了不少坑,这里总结一下:</p>

<ul>
<li><strong>安全性问题</strong>:虽然
localStorage 方便,但它并不是最安全的存储方式。如果你的应用对安全性要求很高,建议使用 IndexedDB 或者后端存储方案。</li>
<li><strong>兼容性问题</strong>:
crypto.getRandomValues 在一些老旧浏览器中可能不支持。如果遇到这种情况,可以考虑使用 polyfill 或者其他替代方案。</li>
<li><strong>用户体验</strong>:生成的密码一定要足够复杂,但也要考虑用户的体验。过于复杂的密码可能会让用户觉得麻烦,建议提供一个复制按钮,方便用户复制密码。</li>
</ul>

<h2>高级技巧:加密存储</h2>
<p>如果你对安全性有更高的要求,可以考虑对存储的密码进行加密。这里我简单介绍一下如何使用
crypto.subtle 进行加密和解密。</p>

<pre class="pure-highlightjs line-numbers language-javascript"><code class="no-highlight language-javascript">async function encrypt(plainText) {
const key = await crypto.subtle.generateKey(
{ name: &#039;AES-GCM&#039;, length: 256 },
true,
[&#039;encrypt&#039;, &#039;decrypt&#039;]
);

const encoded = new TextEncoder().encode(plainText);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: &#039;AES-GCM&#039;, iv: iv },
key,
encoded
);

return { encrypted: Array.from(new Uint8Array(encrypted)), iv: Array.from(iv), key: key };
}

async function decrypt({ encrypted, iv, key }) {
const decrypted = await crypto.subtle.decrypt(
{ name: &#039;AES-GCM&#039;, iv: new Uint8Array(iv) },
key,
new Uint8Array(encrypted)
);

return new TextDecoder().decode(decrypted);
}</code></pre>

<p>这两个函数分别用于加密和解密数据。encrypt 生成一个 AES 密钥并使用 GCM 模式进行加密,decrypt 则用于解密数据。需要注意的是,crypto.subtle 是异步操作,需要使用 await` 关键字。

总结一下

以上是我个人对密码管理器的一些实践经验。这个技术的拓展用法还有很多,比如可以集成到现有的登录系统中,或者扩展成一个完整的密码管理应用。后续我会继续分享这类博客,希望对你有帮助。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
Zz阳阳
Zz阳阳 Lv1
文章里的创新思路很有启发,让我在项目中尝试了新的方法。
点赞 4
2026-02-14 14:25