双重身份验证(Two-factor authentication
)是一种安全机制,它要求用户提供两种不同的身份验证因素来访问他们的帐户:密码和发送到他们的移动设备的验证码。在本文中,我们将一步步通过使用speakeasy
在nodejs
中实现双重身份验证。
首先安装依赖项,我们需要安装express
和speakeasy
:
npm install express speakeasy
创建一个express
服务,并将其配置使用JSON
中间件和静态资源中间件:
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.static('public'));
app.listen(3000, () => {
console.log('Server started on port 3000');
});
创建一个用户模型User
,用于在数据库中存储用户数据。在这个例子中,我们将使用一个简单的数组来存储用户数据:
const users = [];
class User {
constructor(id, name, email, password, secret) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.secret = secret;
}
}
module.exports = { users, User };
创建一个POST
请求的路由用于处理用户的注册操作。在此路由中,我们将为用户生成一个密钥并将其保存在数据库中。我们还会向用户发送一个二维码,用户可以扫描这些代码,以便将帐户添加到他们的应用程序中:
const { users, User } = require('./user');
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');
app.post('/register', (req, res) => {
const { name, email, password } = req.body;
// 为用户生成新的密钥
const secret = speakeasy.generateSecret({ length: 20 });
// 保存用户数据
const user = new User(users.length + 1, name, email, password, secret.base32);
users.push(user);
// 生成一个二维码供用户扫描
QRCode.toDataURL(secret.otpauth_url, (err, image_data) => {
if (err) {
console.error(err);
return res.status(500).send('Internal Server Error');
}
res.send({ qrCode: image_data });
});
});
创建一个POST
请求的路由用于处理用户的登录操作。在此请求中,我们将验证用户的凭证,并需要从用户的应用程序中获得验证码。我们将使用speakeasy
来生成和验证这个验证码:
const { users } = require('./user');
const speakeasy = require('speakeasy');
app.post('/login', (req, res) => {
const { email, password, token } = req.body;
const user = users.find(u => u.email === email);
// 验证用户的凭证
if (!user || user.password !== password) {
return res.status(401).send('Invalid credentials');
}
// 核实用户的令牌
const verified = speakeasy.totp.verify({
secret: user.secret,
encoding: 'base32',
token,
window: 1
});
if (!verified) {
return res.status(401).send('Invalid token');
}
// 用户经过认证
res.send('Login successful');
});
我们通过检查用户是否存在以及他们的密码是否与请求中提供的密码匹配来验证用户的凭证。
如果用户的凭证有效,我们使用speakeasy
来验证二维码。我们传递用户的密钥编码(应该是base32
)、请求中提供的令牌和window: 1
(代表令牌在当前和前一个时间段有效)。
如果令牌无效,我们将返回一个401未经授权的状态码,其中包含消息"无效令牌"。
如果令牌有效,我们将发送一个200OK的状态码,其中包含"成功登录"的消息。此时,用户将进行身份验证,并可以访问应用程序中受保护的资源。
创建一个中间件来验证用户是否已经成功登录。
const speakeasy = require('speakeasy');
exports.requireToken = (req, res, next) => {
const { token } = req.body;
// Find the user with the given email address
const user = users.find(u => u.email === req.user.email);
// Verify the user's token
const verified = speakeasy.totp.verify({
secret: user.secret,
encoding: 'base32',
token,
window: 1
});
if (!verified) {
return res.status(401).send('Invalid token');
}
// 令牌有效,继续到下一个中间件或路由处理程序
next();
}
下面这个路由中添加了requireToken
中间件,需要存在一个有效的2FA令牌才能继续使用:
app.post('/protected', requireToken, (req, res) => {
// 只有当用户的令牌有效时才会调用此路由处理程序
res.send('Protected resource accessed successfully');
});
总之,双重身份验证机制(2FA)是一种强大的安全机制,它为用户帐户增加了额外的保护层。通过要求用户提供两个不同的身份验证因素,例如密码和发送到他们的移动设备的代码,2FA可以帮助防止未经授权访问敏感信息。