如何实现邮箱与手机号双方式登录功能?
解决NestJS中支持邮箱与手机号双方式登录的问题
问题现状
邮箱登录功能正常,但使用手机号登录时失败,日志中发现phoneNumber参数被赋值为verified回调函数,而非前端传入的手机号。
核心问题分析
Passport LocalStrategy的validate方法参数顺序是固定的:默认第一个参数是username(对应配置的usernameField),第二个是password,第三个是Passport内置的done回调函数。你之前在validate中额外添加的phoneNumber参数,实际接收的是这个done回调,而非前端传入的手机号。此外,原配置仅指定了usernameField: 'email',导致无法正确获取手机号参数。
修复步骤
1. 修改LocalStrategy配置,开启请求对象传递
在LocalStrategy的父类构造配置中添加passReqToCallback: true,这样就能在validate方法中获取完整的请求对象,进而读取所有前端传入的参数:
@Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private authService: AuthService) { super({ passReqToCallback: true, // 允许获取request对象 usernameField: 'email', // 保留原配置,不影响后续逻辑 }); }
2. 重构validate方法,从请求体获取参数
调整validate方法的参数,先获取request对象,再从req.body中提取邮箱、手机号和密码,实现动态判断:
async validate(req: Request): Promise<any> { const { email, phoneNumber, password } = req.body; // 校验是否传入了邮箱或手机号 if (!email && !phoneNumber) { throw new UnauthorizedException('请提供邮箱或手机号'); } const user = await this.authService.validateUser(email, password, phoneNumber); if (!user) { throw new UnauthorizedException('Неверный логин или пароль'); } return user; } }
3. 优化AuthService的用户校验逻辑
确保validateUser方法能正确根据传入的邮箱或手机号查询用户,同时处理参数缺失的情况:
async validateUser( email: string, password: string, phoneNumber: string, ): Promise<any> { let user; if (email) { user = await this.usersService.findByEmail(email); } else if (phoneNumber) { user = await this.usersService.findByPhone(phoneNumber); } else { throw new UnauthorizedException('请提供邮箱或手机号'); } if (!user) { throw new UnauthorizedException('Пользователь не найден'); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { throw new UnauthorizedException('Не верный логи или пароль'); } return user; }
前端请求示例
- 邮箱登录请求体:
{ "email": "example@test.com", "password": "your-password" }
- 手机号登录请求体:
{ "phoneNumber": "13800138000", "password": "your-password" }
内容的提问来源于stack exchange,提问作者Максим Дмитриевич
相关产品推荐
相关产品推荐

