前言
短信有长度限制,通常为160字符。如果生成的重置令牌(token)太长,可能会导致短信内容超出限制。为了解决这个问题,可以采取以下措施:
- 使用短的令牌:生成较短的唯一令牌,并将其映射到一个存储在服务器上的长令牌。
- 使用URL缩短服务:将包含长令牌的链接缩短为一个较短的URL。
以下是这两种方法的详细实现步骤。
方法一:使用短的令牌
生成较短的令牌,并将其映射到一个存储在服务器上的长令牌。
1. 安装必要的包
npm install express body-parser twilio bcrypt jsonwebtoken crypto
2. 实现步骤
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const twilio = require('twilio');
const crypto = require('crypto');const app = express();
app.use(bodyParser.json());const users = {}; // 模拟用户数据库
const resetTokens = {}; // 存储短令牌到长令牌的映射// Twilio 设置
const accountSid = 'your-twilio-account-sid';
const authToken = 'your-twilio-auth-token';
const twilioPhoneNumber = 'your-twilio-phone-number';
const client = twilio(accountSid, authToken);// 生成短令牌
function generateShortToken() {return crypto.randomBytes(4).toString('hex'); // 生成8字符长度的令牌
}// 请求重置密码
app.post('/forgot-password', (req, res) => {const { phoneNumber } = req.body;const user = Object.values(users).find(user => user.phoneNumber === phoneNumber);if (!user) {return res.status(400).send('User not found');}const longToken = jwt.sign({ id: user.id }, 'your-secret-key', { expiresIn: '1h' });const shortToken = generateShortToken();resetTokens[shortToken] = longToken;const resetLink = `http://localhost:3000/reset-password?token=${shortToken}`;client.messages.create({body: `Click the link to reset your password: ${resetLink}`,from: twilioPhoneNumber,to: phoneNumber}).then(message => res.status(200).send('Password reset link sent')).catch(error => res.status(500).send('Error sending SMS'));
});// 验证重置令牌并显示重置密码页面
app.get('/reset-password', (req, res) => {const { token } = req.query;if (!token || !resetTokens[token]) {return res.status(400).send('Invalid or expired token');}res.send(`<form action="/reset-password" method="POST"><input type="hidden" name="token" value="${token}" /><input type="password" name="newPassword" placeholder="Enter new password" required /><button type="submit">Reset Password</button></form>`);
});// 更新用户密码
app.post('/reset-password', async (req, res) => {const { token, newPassword } = req.body;if (!token || !resetTokens[token]) {return res.status(400).send('Invalid or expired token');}const longToken = resetTokens[token];try {const { id } = jwt.verify(longToken, 'your-secret-key');const user = users[id];if (!user) {return res.status(400).send('User not found');}const hashedPassword = await bcrypt.hash(newPassword, 10);user.password = hashedPassword;delete resetTokens[token]; // 删除令牌res.status(200).send('Password reset successfully');} catch (error) {res.status(400).send('Invalid or expired token');}
});const PORT = 3000;
app.listen(PORT, () => {console.log(`Server is running on port ${PORT}`);
});
方法二:使用URL缩短服务
使用一个URL缩短服务将包含长令牌的链接缩短为一个较短的URL。这里我们将使用 bitly
作为示例。
1. 安装必要的包
npm install express body-parser twilio bcrypt jsonwebtoken bitly
2. 创建Bitly账户并获取API密钥
前往 Bitly 注册并获取API密钥。
3. 实现步骤
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const twilio = require('twilio');
const { BitlyClient } = require('bitly');const app = express();
app.use(bodyParser.json());const users = {}; // 模拟用户数据库
const resetTokens = {}; // 存储重置令牌// Twilio 设置
const accountSid = 'your-twilio-account-sid';
const authToken = 'your-twilio-auth-token';
const twilioPhoneNumber = 'your-twilio-phone-number';
const client = twilio(accountSid, authToken);// Bitly 设置
const bitly = new BitlyClient('your-bitly-access-token');// 请求重置密码
app.post('/forgot-password', async (req, res) => {const { phoneNumber } = req.body;const user = Object.values(users).find(user => user.phoneNumber === phoneNumber);if (!user) {return res.status(400).send('User not found');}const token = jwt.sign({ id: user.id }, 'your-secret-key', { expiresIn: '1h' });resetTokens[token] = user.id;const resetLink = `http://localhost:3000/reset-password?token=${token}`;try {const response = await bitly.shorten(resetLink);const shortUrl = response.link;await client.messages.create({body: `Click the link to reset your password: ${shortUrl}`,from: twilioPhoneNumber,to: phoneNumber});res.status(200).send('Password reset link sent');} catch (error) {res.status(500).send('Error sending SMS');}
});// 验证重置令牌并显示重置密码页面
app.get('/reset-password', (req, res) => {const { token } = req.query;if (!token || !resetTokens[token]) {return res.status(400).send('Invalid or expired token');}res.send(`<form action="/reset-password" method="POST"><input type="hidden" name="token" value="${token}" /><input type="password" name="newPassword" placeholder="Enter new password" required /><button type="submit">Reset Password</button></form>`);
});// 更新用户密码
app.post('/reset-password', async (req, res) => {const { token, newPassword } = req.body;if (!token || !resetTokens[token]) {return res.status(400).send('Invalid or expired token');}try {const { id } = jwt.verify(token, 'your-secret-key');const user = users[id];if (!user) {return res.status(400).send('User not found');}const hashedPassword = await bcrypt.hash(newPassword, 10);user.password = hashedPassword;delete resetTokens[token]; // 删除令牌res.status(200).send('Password reset successfully');} catch (error) {res.status(400).send('Invalid or expired token');}
});const PORT = 3000;
app.listen(PORT, () => {console.log(`Server is running on port ${PORT}`);
});
总结
通过上述步骤,你可以实现一个安全的重置密码功能,并通过短信发送包含重置令牌的链接给用户。确保在实际应用中使用强加密和合理的安全措施来保护用户信息,同时考虑短信内容长度限制。
还有一种方式使用redis等nosql方式储存,待下篇文章分享