NestJS 如何基于 Google OAuth 访问令牌实现路由 Guard 鉴权
基于Google OAuth访问令牌的路由鉴权Guard实现
核心实现思路
你在OAuth流程中拿到的Google访问令牌是第三方颁发的临时身份凭证,鉴权时不能靠本地存个令牌做字符串匹配——毕竟Google的访问令牌一般1小时就过期,用户还能随时在Google账号设置里撤销,本地存的令牌做比对很容易出现凭证已经失效但接口还能正常访问的漏洞。正确的鉴权逻辑是拦截请求提取携带的访问令牌,验证令牌的合法性后再放行业务请求。
第一步:编写自定义鉴权Guard
新建google-oauth.guard.ts文件,实现NestJS的CanActivate接口,完成请求拦截、令牌提取、有效性校验逻辑:
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; import { Request } from 'express'; @Injectable() export class GoogleOAuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest<Request>(); // 从请求头提取Bearer格式的访问令牌 const authHeader = request.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { throw new UnauthorizedException('未携带有效访问令牌'); } const accessToken = authHeader.split(' ')[1].trim(); // 调用Google官方接口校验令牌合法性 const verifyResult = await fetch( `https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${accessToken}` ).then(res => res.json()).catch(() => { throw new UnauthorizedException('访问令牌校验请求失败'); }); // 令牌无效或过期时直接拦截请求 if (verifyResult.error) { throw new UnauthorizedException('访问令牌无效或已过期'); } // 把校验通过的用户信息挂载到请求对象上,供后续路由逻辑使用 request.user = { email: verifyResult.email, googleUserId: verifyResult.user_id, accessToken: accessToken }; return true; } }
第二步:在业务路由上应用Guard
直接将自定义Guard传入@UseGuards()装饰器即可完成路由保护:
import { GoogleOAuthGuard } from 'src/auth/guards/google-oauth.guard'; @UseGuards(GoogleOAuthGuard) @Get('/get_customer/:id') async findCustomerById(@Param() params, @Request() req): Promise<Customer> { // 可直接从req.user获取已校验的用户信息,做自定义业务权限判断 try { return await this.stripeService.findCustomerById(params.id); } catch (e) { throw new Error(e.message); } }
落地注意事项
- 客户端调用受保护路由时,必须将Google访问令牌以
Authorization: Bearer <访问令牌>的格式放到请求头中,不要将令牌暴露在URL参数、GET请求体中,避免泄露 - 若担心每次请求都调用Google校验接口带来的性能损耗,可以增加本地缓存层:将校验通过的令牌与对应用户信息存入Redis,缓存过期时间设置为比Google令牌有效期短1-2分钟,命中缓存时直接放行,减少外部接口调用
- 如需更细粒度的权限控制(比如校验当前用户是否有权限访问指定ID的客户数据),可以在Guard完成令牌有效性校验后,补充查询本地数据库的权限匹配逻辑,无需在每个控制器方法中重复编写校验代码
内容的提问来源于stack exchange,提问作者Adri HM
相关产品推荐
相关产品推荐

