How to get user's information in Nest.js?

2020-04-17 02:40发布

问题:

I am using Angular+Nest to develop a website. I have created a service(Angular) so that client can get user's information from server when project start up(the same as fresh). Some actions don't need to login, so the login is optional.

What I want is if user has logined, then client should send a request to get user's information.

Server code as below:

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

@Controller('auth')
export class AuthController {
  @Get('getUserInfoByToken')
  async getUserInfoByToken(@RequestUser() user: User): Promise<any> {
    if (user) {
      return {
        nickname: user.nickname,
        level: user.level
      };
    }
  }
}

Howerver, I find there's nothing be return if I don't add @UseGuards(AuthGuard()) as decorator. But if I add it, when project start, this request return 401 as status code. Then the web will turn to login page.

What should I do to avoid this situation? Not every action need login.

回答1:

If you got totally different approach in mind, let me know - will try to help.

Will try to serve a bit more detailed example, which includes passport under the hood. It assumes that passport is used and that Authorization token is being sent.

  • const RegisteredPassportModule = PassportModule.register({ defaultStrategy: 'bearer' })
  • adding HttpStrategy to some AuthModule
  • adding PassportModule.register({ defaultStrategy: 'bearer' }) to imports to AuthModule

Then:

AuthService is a service (and a part of AuthModule) that allows to find given user via token sent via token passed via Authorization header, directly from the database.

import { Strategy } from 'passport-http-bearer';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class HttpStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super()
  }

  async validate(token: string) {
    const user = await this.authService.findUserByToken(token);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

Usage is after all quite simple (you can put the guard for either method of controller):

@UseGuards(AuthGuard())
@Get()
someMethod(@Req() request: RequestWithUser, ...) {
  // ...
}

Where RequestWithUser is just:

import { User as UserEntity } from '../../models/user.entity'

export type RequestWithUser = Request & { user: UserEntity }

and the /user endpoint would be just returning request.user

I hope this helps!



回答2:

If you really insist on this way (see comments), you can use Interceptors:


@Injectable()
export class GetUserInterceptor implements NestInterceptor {
  constructor(private readonly authService: AuthService) {
  }

  async intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest()
    const item = await this.authService.getByToken(/* extract me from headers*/)
    request.user = item
    return next.handle()
  }
}

so AuthGuard is not needed.



回答3:

Write a conditional logic in the AuthGuard to check if a user is provided in the request.