Passport.js Local Strategy实现手机号+邮箱登录的疑问
实现Passport.js Local Strategy支持邮箱/手机号登录的解决方案
首先直接回答你的问题:不需要添加第三个字段,但需要调整Mongoose模型的配置和Passport Strategy的验证逻辑。passport-local-mongoose默认只认一个usernameField,但我们可以自定义验证逻辑,判断用户输入的是邮箱还是手机号,再去数据库匹配用户。
步骤1:调整Mongoose模型
你的现有模型已经包含email和phoneNumber字段,但需要优化字段约束,同时保留passport-local-mongoose的密码处理能力:
import mongoose from 'mongoose'; import passportLocalMongoose from 'passport-local-mongoose'; const Schema = mongoose.Schema; const AccountSchema = new Schema({ phoneNumber: { type: String, unique: true, sparse: true // 允许字段为空,但非空时必须唯一 }, email: { type: String, unique: true, sparse: true }, myId: String, termsandcondition: Boolean, verifiedEmailCode: String, verifiedEmail: { type: Boolean, default: false }, verifiedPhone: { type: Boolean, default: false } }); // 配置passport-local-mongoose,保留默认的usernameField但后续会自定义验证逻辑 AccountSchema.plugin(passportLocalMongoose, { usernameField: 'email' }); export default mongoose.model('Account', AccountSchema);
步骤2:自定义Passport Local Strategy验证逻辑
原来的Account.authenticate()是默认逻辑,只能匹配单一字段。我们需要重写验证函数,判断用户输入的是邮箱还是手机号:
const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; const Account = require('./model/account'); passport.use(new LocalStrategy({ usernameField: 'loginIdentifier', // 前端统一传这个字段,值可以是邮箱或手机号 passwordField: 'password' }, async (loginIdentifier, password, done) => { try { // 判断输入类型:包含@则为邮箱,否则为手机号 const user = loginIdentifier.includes('@') ? await Account.findOne({ email: loginIdentifier }) : await Account.findOne({ phoneNumber: loginIdentifier }); if (!user) { return done(null, false, { message: '用户不存在' }); } // 用passport-local-mongoose内置的密码验证方法 const isPasswordValid = await user.authenticate(password); if (!isPasswordValid) { return done(null, false, { message: '密码错误' }); } return done(null, user); } catch (err) { return done(err); } })); passport.serializeUser(Account.serializeUser()); passport.deserializeUser(Account.deserializeUser());
步骤3:调整登录路由适配新字段
前端登录时需要将原来的email参数改为loginIdentifier(或者你也可以让前端同时传email/phoneNumber,后端优先判断,但统一字段更清晰):
api.post('/login', (req, res, next) => passport.authenticate( 'local', { session: false, scope: [] }, (err, user, info) => { if (err) { return next(err); } if (!user) { return res.json({ "status": false, "code": 401, "message": info.message || '登录失败' }); } if (!user.verifiedEmail && !user.verifiedPhone) { return res.json({ "status": false, "code": 403, "message": "You need to verify your email or Phone number" }); } req.logIn(user, next); } )(req, res, next), generateAccessToken, respond);
额外说明
- 注册逻辑可以同步优化:允许用户选择用邮箱或手机号注册,注册时对应填充
email或phoneNumber字段,username可以复用其中一个(或者留空,passport-local-mongoose会自动处理,但建议保持一致性)。 - 字段唯一性约束用
sparse: true是为了允许用户只填邮箱或手机号中的一个,避免强制要求两个字段都存在。
内容的提问来源于stack exchange,提问作者elizabeth
相关产品推荐
相关产品推荐

