在没有 AuthGuard 的路由上获取 nestjs 中的当前用户
Posted
技术标签:
【中文标题】在没有 AuthGuard 的路由上获取 nestjs 中的当前用户【英文标题】:Get current user in nestjs on a route without an AuthGuard 【发布时间】:2020-11-25 04:46:31 【问题描述】:我使用带有 jwt 策略的带有护照的nestjs。我想在我的一些请求中获得当前用户。 目前,我有一个看起来像这样的装饰器:
import createParamDecorator, ExecutionContext from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: string, ctx: ExecutionContext) =>
const user = ctx.switchToHttp().getRequest().user;
if (!user)
return null;
return data ? user[data] : user; // extract a specific property only if specified or get a user object
,
);
当我在带有 AuthGuard 的路线上使用它时,它会按预期工作:
@Get('test')
@UseGuards(AuthGuard())
testRoute(@CurrentUser() user: User)
console.log('Current User: ', user);
return user ;
但是我如何使它在非保护路线上工作(获取当前用户)?我需要用户能够发布他们的 cmets,无论他们是否获得授权,但是,当他们登录时,我需要获取他们的姓名。
基本上,我需要一种方法在每个(或至少在一些非 AuthGuard 的请求)上传播 req.user,通过应用护照中间件以快递方式进行确实很简单,但我不确定如何使用@nestjs/passport 来做到这一点。
[编辑] 感谢 vpdiongzon 为我指明了正确的方向,我根据他的回答选择了一个守卫,它只是用 user 或 null 填充 req.user:
import Injectable from '@nestjs/common';
import AuthGuard from '@nestjs/passport';
@Injectable()
export class ApplyUser extends AuthGuard('jwt')
handleRequest(err: any, user: any)
if (user) return user;
return null;
现在我可以在任何需要获取当前用户的不受保护的路线上使用它
@Get('me')
@UseGuards(ApplyUser)
me(@CurrentUser() user: User)
return user ;
【问题讨论】:
我认为可以直接使用me(@Request() req) return req.user
,它将为我们提供当前用户的所有详细信息。
【参考方案1】:
您需要将 AuthGuard 应用到每条路由,但如果您有不需要身份验证的路由,只需添加自定义装饰器,例如:
身份验证卫士
export class JwtAuthGuard extends AuthGuard('jwt')
constructor(private readonly reflector: Reflector)
super();
handleRequest(err, user, info, context)
const request = context.switchToHttp().getRequest();
const allowAny = this.reflector.get<string[]>('allow-any', context.getHandler());
if (user) return user;
if (allowAny) return true;
throw new UnauthorizedException();
在 app.module.js 中全局应用 AuthGuard
import APP_GUARD, Reflector from '@nestjs/core';
import AppController from './app.controller';
import JwtAuthGuard from './app.guard';
@Module(
imports: ],
controllers: [AppController],
providers: [
provide: APP_GUARD,
useFactory: ref => new JwtAuthGuard(ref),
inject: [Reflector],
,
AppService,
],
)
export class AppModule
自定义装饰器允许无需身份验证的路由
import SetMetadata from '@nestjs/common';
export const AllowAny = () => SetMetadata('allow-any', true);
在路由中应用 AllowAny,如果 AllowAny 装饰器未附加在控制器路由中,则需要用户。
@Post('testPost')
@AllowAny()
async testPost(@Req() request)
console.log(request.user)
【讨论】:
只是一个提示:最好返回null,因为它将是用户值:if (allowAny) return null;【参考方案2】:"Basically, I need a way to propagate req.user on every(or at least on some of not AuthGuard'ed request), it is realy straight forward to do in express by applying passport middleware, but im not sure how to do it with @nestjs/passport."
为了实现这一点,我们编写了一个拦截器,因为我们需要使用 UsersService。 UserService 是依赖注入系统的一部分。我们不能只导入用户服务并自己创建一个新实例。该服务使用用户存储库,并且用户存储库仅通过依赖注入设置。
问题是我们不能使用带有参数装饰器的依赖注入。此装饰器无法以任何方式进入系统并尝试访问其中的某些实例。这就是我们编写拦截器的方式。我在代码上制作 cmets:
// this interceptor will be used by the custom param decoratro to fetch the current User
import NestInterceptor,ExecutionContext,CallHandler,Injectable from '@nestjs/common';
import UsersService from '../users.service';
@Injectable()
// "implements" guide us how to put together an interceptor
export class CurrentUserInterceptor implements NestInterceptor
constructor(private userService: UsersService)
// handler refers to the route handler
async intercept(context: ExecutionContext, handler: CallHandler)
const request = context.switchToHttp().getRequest();
const userId = request.session || ;
if (userId)
const user = await this.userService.findOne(userId);
// we need to pass this down to the decorator. SO we assign the user to request because req can be retrieved inside the decorator
// ------THIS IS WHAT YOU WANTED--------
request.currentUser = user;
// run the actual route handler
return handler.handle();
现在您需要将其注册到模块中:
@Module(
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService, AuthService, CurrentUserInterceptor],
)
内部控制器:
@Controller('auth')
@UseInterceptors(CurrentUserInterceptor)
export class UsersController
constructor("inject services)
@Get('/me')
me(@CurrentUser() user: User)
return user;
在您使用 CurrentUser 参数装饰器的任何路由处理程序中,您都可以访问“用户”。
您实际上不需要编写自定义参数装饰器
你可以只使用拦截器,它的实现会有所不同:
@Get('/me')
me(@CurrentUserInterceptor() request: Request)
// You have access to request.currentUser
return request.currentUser
全局设置拦截器
拦截器的当前设置很繁琐。我们一次将拦截器应用于一个控制器。 (这称为受控范围)相反,您可以全局使此拦截器可用:
用户模块:
import APP_INTERCEPTOR from '@nestjs/core';
@Module(
// this createes repository
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [
UsersService,
AuthService,
provide: APP_INTERCEPTOR,
useClass: CurrentUserInterceptor,
,
],
)
这种方法有一个缺点。不是每个控制器都关心当前用户是什么。在这些控制器中,您仍然需要向数据库发出请求以获取当前用户。
【讨论】:
【参考方案3】:解析后的用户信息存储在request.user
import Req from '@nestjs/common'
import Request from 'express'
@Post()
create(@Req() request: Request)
console.log('user', request.user)
【讨论】:
以上是关于在没有 AuthGuard 的路由上获取 nestjs 中的当前用户的主要内容,如果未能解决你的问题,请参考以下文章
Nest.js 中使用 @nestjs/passport 的可选身份验证
httpOnly cookie 中的 JWT - AuthGuard 和受保护的路由(Node.js、Angular)