短信认证开发疑问:如何在控制器不同方法间共享验证码数据
Hey there! Let's break down your problem first—since /sendsms and /confirm_sms are two totally separate HTTP requests, next() won't help here (that's only for passing control along the middleware chain of a single request). You need a way to store the generated auth code between these two requests, and here are some practical, production-ready ways to do it:
The core idea is to link the auth code with the user's phone number and store this pair temporarily. For development, you can use an in-memory Map (but note this resets when your server restarts—never use this in production). For production, Redis is ideal because it supports automatic key expiration, which is perfect for time-sensitive auth codes.
Development Example (In-Memory Storage)
// Add this at the top of your controller file const authCodeStore = new Map(); // Key: phone number, Value: auth code module.exports.send_post = (req, res, next) => { const client_phone_number = req.body.phoneNum; const client_auth_number = create_auth_num(); // Store the code with a 5-minute expiration (adjust as needed) authCodeStore.set(client_phone_number, client_auth_number); setTimeout(() => { authCodeStore.delete(client_phone_number); }, 5 * 60 * 1000); send_message(client_phone_number, client_auth_number); // ✅ Don't return the auth code to the frontend! That's a huge security risk. res.json({ message: "Verification code sent" }); }; module.exports.confirm_sms_post = (req, res) => { const client_phone_number = req.body.phoneNum; // Make sure frontend sends the phone number here too const client_auth_number = req.body.auth; const server_auth_number = authCodeStore.get(client_phone_number); if (server_auth_number && client_auth_number === server_auth_number) { authCodeStore.delete(client_phone_number); // Delete after successful verification to prevent reuse res.json("OK."); } else { res.json("False."); } };
Production Example (Redis)
First, install Redis and the client package (npm install redis), then:
const redis = require('redis'); const redisClient = redis.createClient({ /* your Redis config */ }); module.exports.send_post = async (req, res, next) => { const client_phone_number = req.body.phoneNum; const client_auth_number = create_auth_num(); // Store in Redis with a 5-minute expiration (300 seconds) await redisClient.setEx(client_phone_number, 300, client_auth_number); send_message(client_phone_number, client_auth_number); res.json({ message: "Verification code sent" }); }; module.exports.confirm_sms_post = async (req, res) => { const client_phone_number = req.body.phoneNum; const client_auth_number = req.body.auth; const server_auth_number = await redisClient.get(client_phone_number); if (server_auth_number && client_auth_number === server_auth_number) { await redisClient.del(client_phone_number); res.json("OK."); } else { res.json("False."); } };
If your app uses sessions (like express-session), you can store the auth code directly in the user's session. This works well if users are already logged in, or if you use anonymous sessions.
module.exports.send_post = (req, res, next) => { const client_phone_number = req.body.phoneNum; const client_auth_number = create_auth_num(); // Store auth details in the session req.session.smsAuth = { phone: client_phone_number, code: client_auth_number, expires: Date.now() + 5 * 60 * 1000 }; send_message(client_phone_number, client_auth_number); res.json({ message: "Verification code sent" }); }; module.exports.confirm_sms_post = (req, res) => { const client_phone_number = req.body.phoneNum; const client_auth_number = req.body.auth; const sessionAuth = req.session.smsAuth; // Check if session data exists, is valid, and hasn't expired if (!sessionAuth || sessionAuth.phone !== client_phone_number || Date.now() > sessionAuth.expires) { return res.json("False."); } if (client_auth_number === sessionAuth.code) { delete req.session.smsAuth; // Clean up after success res.json("OK."); } else { res.json("False."); } };
You can encrypt the auth code + phone number into a token, return it to the frontend, and have the frontend send it back with the verification request. This avoids server-side storage but has security risks (if the token is intercepted, attackers can reuse it).
const crypto = require('crypto'); const SECRET_KEY = 'your-strong-secret-key'; // Use a secure, environment-stored key in production module.exports.send_post = (req, res, next) => { const client_phone_number = req.body.phoneNum; const client_auth_number = create_auth_num(); // Create encrypted token with expiration const authData = JSON.stringify({ phone: client_phone_number, code: client_auth_number, expires: Date.now() + 5 * 60 * 1000 }); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(SECRET_KEY), iv); let encrypted = cipher.update(authData); encrypted = Buffer.concat([encrypted, cipher.final()]); res.json({ message: "Verification code sent", token: `${iv.toString('hex')}:${encrypted.toString('hex')}` }); }; module.exports.confirm_sms_post = (req, res) => { const client_auth_number = req.body.auth; const token = req.body.token; if (!token) return res.json("False."); // Decrypt the token const [ivHex, encryptedHex] = token.split(':'); const iv = Buffer.from(ivHex, 'hex'); const encryptedText = Buffer.from(encryptedHex, 'hex'); const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(SECRET_KEY), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); try { const authData = JSON.parse(decrypted.toString()); if (Date.now() > authData.expires) return res.json("False."); if (authData.code === client_auth_number) { res.json("OK."); } else { res.json("False."); } } catch (err) { res.json("False."); } };
Critical Notes:
- Never return the auth code directly to the frontend (your original
send_postdoes this—fix that immediately, as it bypasses the entire SMS verification purpose). - Always set an expiration time for auth codes to prevent them from being used indefinitely.
- Delete the auth code from storage immediately after successful verification to stop reuse.
内容的提问来源于stack exchange,提问作者Yeon

