如何在NestJS谷歌登录URL传递查询参数并在回调中使用
解决NestJS谷歌认证中传递查询参数到回调的问题
要实现从登录触发URL传递查询参数到回调方法,核心是利用OAuth2的state参数携带自定义数据,通过重写Guard捕获参数、在Strategy中解析传递,最终在回调方法中使用。
步骤1:重写GoogleAuthGuard捕获查询参数
修改Guard类,重写getAuthenticateOptions方法,从请求中提取需要传递的查询参数,封装到state中(谷歌OAuth会原封不动将state带回回调):
import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ExecutionContext } from '@nestjs/common'; @Injectable() export class GoogleAuthGuard extends AuthGuard('google') { getAuthenticateOptions(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); // 示例捕获`redirectTo`参数,可替换为你需要的参数名 const customParams = { redirectTo: request.query.redirectTo, // 可添加更多自定义参数 }; // 转成JSON字符串存入state,敏感参数建议加密 return { state: JSON.stringify(customParams) }; } }
步骤2:修改GoogleStrategy接收并传递参数
更新Strategy的validate方法,从回调请求中获取谷歌带回的state,解析后和用户信息一起返回,供后续回调方法使用:
import { PassportStrategy } from '@nestjs/passport'; import { Strategy, Profile, VerifiedCallback } from 'passport-google-oauth20'; import { ConfigService } from '@nestjs/config'; import { ConfigEnum } from '../config/enum/config.enum'; import { Request } from 'express'; export class GoogleStrategy extends PassportStrategy(Strategy) { constructor(private configService: ConfigService) { super({ clientID: configService.get(ConfigEnum.CLIENT_ID), clientSecret: configService.get(ConfigEnum.CLIENT_SECRET), callbackURL: configService.get('CALLBACK_URL'), scope: ['profile', 'email'], passReqToCallback: true, // 开启后可获取请求对象 }); } async validate( req: Request, accessToken: string, refreshToken: string, profile: Profile, done: VerifiedCallback, ): Promise<any> { const email = profile.emails[0].value; // 解析谷歌带回的state,获取自定义参数 const state = req.query.state as string; const customParams = JSON.parse(state); // 将用户信息和自定义参数一起返回 done(null, { email, ...customParams }); } }
步骤3:在回调方法中使用参数
修改回调控制器方法,从req.user中提取传递过来的查询参数并使用:
import { Get, Req, Res, UseGuards } from '@nestjs/common'; import { Response, Request } from 'express'; import { GoogleAuthGuard } from './google-auth.guard'; import { AuthService } from './auth.service'; @Get('google/login') @UseGuards(GoogleAuthGuard) handleGoogleLogin() {} @Get('google/redirect') @UseGuards(GoogleAuthGuard) async handleGoogleRedirect(@Req() req: Request, @Res() res: Response) { // 从req.user中获取自定义参数和用户信息 const { email, redirectTo } = req.user as any; const tokens = await this.authService.signInWithGoogle(req); // 使用自定义参数,比如跳转到指定地址 const targetUrl = redirectTo ? `${redirectTo}?tokens=${tokens}` : 'http://localhost:8080?tokens=' + tokens; res.redirect(302, targetUrl); }
步骤4:配置Session中间件
Passport需要Session存储认证临时数据,在main.ts中添加Session配置:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as session from 'express-session'; import * as passport from 'passport'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 配置Session,生产环境请更换密钥并开启secure cookie app.use(session({ secret: 'your-session-secret-key', resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 3600000 // 1小时有效期 }, })); app.use(passport.initialize()); app.use(passport.session()); await app.listen(3000); } bootstrap();
安全提示
如果传递敏感参数,建议对state进行加密处理,避免参数被篡改或泄露。可使用Node.js的crypto模块实现AES加密解密。
内容的提问来源于stack exchange,提问作者Chandradeep Kumar
相关产品推荐
相关产品推荐

