客户端socket.remotePort跨路由变化是否正常?如何维持稳定?
问题
我正在开发一个Node.js应用,想要用客户端IP、端口号和邮箱地址生成唯一哈希。在signup路由的处理器中,req.socket.remotePort在GET和POST请求里保持一致,但切换到authorize路由时,req.socket.remotePort会递增(总是前一个端口号+1)。
请问这是预期现象吗?有没有办法让remotePort保持稳定一致?
signup.js路由代码
var express = require('express'); var lib = require('../lib.js'); var router = express.Router(); var nodemailer = require('nodemailer'); router.get('/', function(req, res, next) { if(!Object.hasOwn(req.params, 'email')) { req.params.email = ""; } res.render('signup', { title: 'Signup', email: req.params.email, ip: req.socket.remoteAddress, port: req.socket.remotePort }); }); router.post('/', function(req, res, next) { var transporter = nodemailer.createTransport({ service: 'gmail', auth: { user: 'authorize@example.com', pass: 'abc123' }}); var hashCode = lib.hashCode(req.body.email + req.socket.remoteAddress + req.socket.remotePort); var mailOptions = { from: 'authorize@example.com', to: req.body.email, subject: 'Activiation Code', text: 'Your activiation code is: ' + hashCode }; transporter.sendMail(mailOptions, function(error, info){ if (error) { console.log(error); } else { console.log('Email sent: ' + info.response); res.redirect('/authorize?email=' + req.body.email); } }); }); module.exports = router;
Authorization路由代码
var express = require('express'); var lib = require('../lib.js'); var router = express.Router(); router.get('/', function(req, res, next) { if(!Object.hasOwn(req.query, 'authCode')) { req.query.authCode = ""; } res.render('authorize', { title: 'Authorize', email: req.query.email, ip: req.socket.remoteAddress(), port: req.socket.remotePort, authCode: req.query.authCode }); }); router.post('/', function(req, res, next) { // 问题出在这里 // remotePort现在和之前不一样了(比上次的端口号+1) // 导致生成的哈希总是不同,验证失败 var hashCode = lib.hashCode(req.query.email + req.socket.remoteAddress + req.socket.remotePort); if(hashCode != req.body.authCode) { console.log('Invalid') } else { console.log('Valid') } }); module.exports = router;
解答
这是预期现象
HTTP是无状态协议,浏览器每次发起新请求(比如从signup跳转至authorize的请求)都会建立新的TCP连接。客户端端口属于临时端口,操作系统会为每个新TCP连接分配未被占用的临时端口,因此切换路由时端口变化是正常行为。而signup里的GET和POST请求可能复用了同一个TCP连接(HTTP/1.1默认开启连接复用),所以端口保持一致,但跳转后的请求是全新连接,端口自然会改变。
解决方法
由于客户端端口本身不稳定,不适合作为哈希生成的因子,推荐以下几种方案:
改用Session存储关键信息
在signup的POST请求中,把生成哈希的核心信息(邮箱、IP)或直接把哈希值存入用户Session,验证时从Session中读取比对:// 修改signup的POST逻辑 router.post('/', function(req, res, next) { // ... 邮件发送相关代码 var userKey = req.body.email + req.socket.remoteAddress; var hashCode = lib.hashCode(userKey); // 存入session req.session.auth = { email: req.body.email, validCode: hashCode }; // ... 发送邮件并跳转 }); // 修改authorize的POST逻辑 router.post('/', function(req, res, next) { if (!req.session.auth || req.session.auth.validCode !== req.body.authCode) { console.log('Invalid'); } else { console.log('Valid'); // 验证通过后可清除session数据 delete req.session.auth; } });存储到数据库/缓存
生成哈希后,将哈希与对应的邮箱、IP存入数据库或Redis。验证时根据邮箱查询存储的哈希值,再和用户输入的代码比对,无需重新生成哈希。仅用邮箱+IP生成哈希
如果必须保留IP因子,可以去掉端口,仅用邮箱和客户端IP生成哈希。但要注意:同一公网下的多个用户可能共享IP,这种场景需要补充其他验证逻辑。
内容的提问来源于stack exchange,提问作者mr.b
相关产品推荐
相关产品推荐

