網站搜尋

javascript - nestjs 中的可選身份驗證


我想知道是否有一個裝飾器可以在控制器方法中使 req.user 對象可用,如果使用者已登入(發送了授權標頭),如果沒有則讓 req.user 為 null。

AuthGuard 裝飾器如果使用者未登入會回傳 401,因此它不適合我的情況。

答案#1

沒有內建的裝飾器,但你可以很容易地自己創建一個。請參閱文件中的範例:

import { createParamDecorator } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

export const User = createParamDecorator((data, req) => {
  return req.user;
});

由於內建的 AuthGuard 會拋出異常,你可以創建自己的版本並覆寫請求處理程序:

@Injectable()
export class MyAuthGuard extends AuthGuard('jwt') {

  handleRequest(err, user, info) {
    // no error is thrown if no user is found
    // You can use info for logging (e.g. token is expired etc.)
    // e.g.: if (info instanceof TokenExpiredError) ...
    return user;
  }

}

確保你在 JwtStrategy 中沒有拋出錯誤:

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'secretKey',
    });
  }

  async validate(payload) {
    const user = await this.authService.validateUser(payload);
    // in the docs an error is thrown if no user is found
    return user;
  }
}

然後你可以像這樣在你的 Controller 中使用它:

@Get()
@UseGuards(MyAuthGuard)
getUser(@User() user) {
  return {user};
}

答案#2

另一種做法是建立一個匿名通行證策略:

// In anonymous.strategy.ts
@Injectable()
export class AnonymousStrategy extends PassportStrategy(Strategy, 'anonymous') {
  constructor() {
    super()
  }

  authenticate() {
    return this.success({})
  }
}

然後,在控制器中鏈接這個策略:

// In create-post.controller.ts
@Controller()
export class CreatePostController {
  @UseGuards(AuthGuard(['jwt', 'anonymous'])) // first success wins
  @Post('/posts')
  async createPost(@Req() req: Request, @Body() dto: CreatePostDto) {
    const user = req.user as ExpressUser

    if (user.email) {
      // Do something if user is authenticated
    } else {
      // Do something if user is not authenticated
    }
    ...
  }
}