NestJS+MongoDB中阻止本地注册用户通过Google登录及Google登录失败后重定向前端的问题
NestJS+MongoDB中阻止本地注册用户通过Google登录及Google登录失败后重定向前端的问题
看起来你已经抓到了核心问题——本地注册的用户(authProvider为local)不应该能通过Google OAuth登录成功,但你的判断逻辑只覆盖了本地密码登录的场景,漏掉了Google登录这条流程的校验,所以才会出现绕过的情况。我来帮你一步步解决这个问题:
一、核心问题分析
你的verifyUser方法确实在本地登录时检查了authProvider,但Google登录的流程是独立的:它通过GoogleAuthGuard触发OAuth授权,之后在回调路由里处理用户信息,而你目前的代码里没有在Google登录的回调逻辑中添加“检查用户是否为本地注册”的判断,所以只要邮箱匹配,不管是不是本地用户都能登录成功。
二、具体解决方案
1. 完善Google登录的回调处理逻辑
首先在AuthService中添加专门处理Google登录的方法,在允许登录前强制校验用户的authProvider:
// auth.service.ts 中新增方法 async googleLogin(userProfile: { email: string; name: string }, response: Response) { // 1. 根据Google返回的邮箱查找现有用户 const existingUser = await this.usersService.getUser({ email: userProfile.email }); // 2. 关键校验:如果用户已存在且是本地注册用户,直接阻止登录 if (existingUser) { if (existingUser.authProvider === 'local') { throw new BadRequestException('该邮箱已通过密码注册,请使用密码登录'); } else if (existingUser.authProvider === 'google') { // 是已注册的Google用户,直接执行登录 await this.login(existingUser, response, true); return; } } // 3. 如果是新用户,创建Google类型的账号 const newGoogleUser = await this.userModel.create({ email: userProfile.email, name: userProfile.name, authProvider: 'google', // 标记为Google登录用户 password: null, // 注意:Google用户不需要密码,根据你的User schema调整(如果schema要求必填,可以设为空字符串) }); // 4. 登录新创建的Google用户 await this.login(newGoogleUser, response, true); }
2. 补全Google登录的回调路由配置
在AuthController中添加Google授权的触发路由和回调路由,同时处理错误重定向:
// auth.controller.ts 中新增路由 @Get('google') @UseGuards(GoogleAuthGuard) async googleAuth() { // 触发Google OAuth授权,Guard会自动跳转至Google登录页,无需额外逻辑 } @Get('google/callback') @UseGuards(GoogleAuthGuard) async googleAuthCallback( @CurrentUser() userProfile: { email: string; name: string }, @Res({ passthrough: true }) response: Response ) { try { // 调用刚才新增的googleLogin方法处理登录 await this.authService.googleLogin(userProfile, response); } catch (error) { // 登录失败时,重定向到前端登录页并携带错误信息 const errorMsg = encodeURIComponent(error.message); response.redirect(`${this.configService.getOrThrow('FRONTEND_URL')}/login?error=${errorMsg}`); } }
3. 调整GoogleAuthGuard的配置
确保GoogleAuthGuard能正确传递用户的邮箱和名称,同时配置默认的失败重定向:
// google-auth.guard.ts import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ConfigService } from '@nestjs/config'; @Injectable() export class GoogleAuthGuard extends AuthGuard('google') { constructor(private configService: ConfigService) { super({ clientID: configService.getOrThrow('GOOGLE_CLIENT_ID'), clientSecret: configService.getOrThrow('GOOGLE_CLIENT_SECRET'), callbackURL: configService.getOrThrow('GOOGLE_CALLBACK_URL'), scope: ['email', 'profile'], // 授权时获取邮箱和昵称 failureRedirect: `${configService.getOrThrow('FRONTEND_URL')}/login?error=Google授权失败`, // 授权失败时的默认重定向 }); } // 重写validate方法,提取Google返回的用户核心信息 async validate(payload: any) { return { email: payload.emails[0].value, name: payload.displayName, }; } }
4. 前端配合处理错误提示
当重定向到前端/login?error=xxx时,前端可以读取URL参数中的error字段,显示对应的提示信息(比如“该邮箱已通过密码注册,请使用密码登录”),提升用户体验。
三、额外注意事项
- 确保你的
UserSchema中authProvider字段是必填项,默认值设为local(对应本地注册用户),比如:// user.schema.ts @Schema() export class User { // ...其他字段 @Prop({ required: true, enum: ['local', 'google'], default: 'local' }) authProvider: string; } - 如果你之前的Google登录逻辑中存在“自动创建用户”的代码,一定要替换成上面的校验逻辑,避免绕过检查。
内容来源于stack exchange
相关产品推荐
相关产品推荐

