短信字符有限?教你优化忘记密码链接长度的方法

34 篇文章 0 订阅
3 篇文章 0 订阅

前言

短信有长度限制,通常为160字符。如果生成的重置令牌(token)太长,可能会导致短信内容超出限制。为了解决这个问题,可以采取以下措施:

  1. 使用短的令牌:生成较短的唯一令牌,并将其映射到一个存储在服务器上的长令牌。
  2. 使用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方式储存,待下篇文章分享

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值