从零搭建安全可靠的密码管理器全攻略
先看效果,再看代码
最近在项目里捣鼓密码管理器,折腾了半天终于搞定了一个还算不错的实现。这个密码管理器可以用在一些需要用户输入和存储密码的场景,比如登录、注册等。亲测有效,下面直接上代码。
核心代码就这几行
首先,我们来看一下最核心的部分:如何生成一个随机的强密码。这里我用的是 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('userPasswords')) || {};
userPasswords[username] = password;
localStorage.setItem('userPasswords', JSON.stringify(userPasswords));
}
function getPassword(username) {
const userPasswords = JSON.parse(localStorage.getItem('userPasswords')) || {};
return userPasswords[username] || null;
}</code></pre>
<p>这两个函数分别用于保存和获取用户的密码。
savePassword 将用户名和密码以 JSON 格式存入 localStorage,getPassword 则从 localStorage 中读取密码。</p>
<h2>前端展示和交互</h2>
<p>接下来是前端部分,我们需要一个简单的表单来让用户输入用户名,并生成并显示密码。</p>
<pre class="pure-highlightjs line-numbers language-html"><code class="no-highlight language-html"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Manager</title>
</head>
<body>
<h1>Password Manager</h1>
<form id="passwordForm">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<br><br>
<button type="button" onclick="generateAndSavePassword()">Generate and Save Password</button>
<br><br>
<label for="generatedPassword">Generated Password:</label>
<input type="text" id="generatedPassword" readonly>
</form>
<script src="passwordManager.js"></script>
</body>
</html></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('username').value;
const password = generateStrongPassword();
savePassword(username, password);
document.getElementById('generatedPassword').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: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
const encoded = new TextEncoder().encode(plainText);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', 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: 'AES-GCM', iv: new Uint8Array(iv) },
key,
new Uint8Array(encrypted)
);
return new TextDecoder().decode(decrypted);
}</code></pre>
<p>这两个函数分别用于加密和解密数据。
encrypt 生成一个 AES 密钥并使用 GCM 模式进行加密,decrypt 则用于解密数据。需要注意的是,crypto.subtle 是异步操作,需要使用 await` 关键字。
总结一下
以上是我个人对密码管理器的一些实践经验。这个技术的拓展用法还有很多,比如可以集成到现有的登录系统中,或者扩展成一个完整的密码管理应用。后续我会继续分享这类博客,希望对你有帮助。