前言
短信有长度限制,通常为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方式储存,待下篇文章分享