로그인을 할 때 그 로그인한 고유 유저를 위한 토큰을 생성해야 하는데 그 토큰을 생성할 때 JWT
라는 모듈을 사용
Header
Payload
Verify Signature
payload
+ Verify Signature
의 secret key
를 해시 알고리즘을 통해 생성한다.jwt
모듈과 passport
모듈을 함께 사용해서 토큰 생성passport
는 jwt
를 이용하여 인증 처리하는 과정을 쉽게 만들어줌모듈 생성 @nestjs/jwt
@nestjs/passport
passport
passport-jwt
npm install @nestjs/jwt @nestjs/passport passport passport-jwt --save
auth.module.ts
JwtModule.register({
secret: 'Secret1234',
signOptions: {
expiresIn: 60 * 60,
},
}),
auth.module.ts
PassportModule.register({ defaultStrategy: 'jwt' }),
auth.service.ts
private jwtService: JwtService,
Token을 만드려면 Secret
과 Payload
가 필요
Payload
에는 자신이 전달하고자 하는 정보를 넣어줌
ex) Role
, 유저 이름
, 이메일
....단, 민감한 정보는 넣으면 안됨
Payload를 이용해서 JWT에서 토큰을 만들 때 사용하는 Sign
메소드를 이용해서 토큰을 생성
async signIn(
authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string }> // 추가
// 유저 토큰 생성 (Secret + Payload)
const payload = { username };
const accessToken = await this.jwtService.sign(payload);
return { accessToken };
auth.controller.ts
@Post('/signin')
signIn(
@Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string }> { // 추가
return this.authService.signIn(authCredentialsDto);
}
1-4번
=> 지난 시간 /5-6번
=> 오늘 해야할 부분
auth.controller.ts
@Post('/signin')
signIn(
@Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string }> {
return this.authService.signIn(authCredentialsDto);
}
auth.service.ts
const accessToken = await this.jwtService.sign(payload);
auth.service.ts
return { accessToken };
i
)
서버에서 그 토큰이 유효(valid)한 것인지 체크
ii
)
만약 유효한 것이면payload
안에 들어있는username
을 이용해서 데이터베이스에 있는 유저인지 체크
iii
)
있는 유저라면 유저 객체를 데이터베이스에서 가져오고, 없는 유저라면 에러를 보냄
@types/passport-jwt
npm i @types/passport-jwt --save
jwt.strategy.ts
파일 생성import { Injectable } from '@nestjs/common';
import { UnauthorizedException } from '@nestjs/common/exceptions';
import { PassportStrategy } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from './user.entity';
import { UserRepository } from './user.repository';
@Injectable() // 어디에서나 주입해서 사용 가능하게 하기 위함
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
) {
super({
// 부모 컨포넌츠를 사용하기 위함
secretOrKey: 'Secret1234', // 토큰이 유효한지 체크, authmodule에 있는 값과 같음
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
// 토큰을 어디서 가져와서 검증을 하는지
// bearer 토큰 타입으로 가져옴
});
}
// 인증 성공 후
async validate(payload) {
// 토큰에 payload가 들어옴
const { username } = payload;
const user: User = await this.userRepository.findOneBy({ username });
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
@Injectable()
: 어디에서나 주입해서 사용 가능하게 하기 위함super
: 부모 컨포넌츠를 사용하기 위함secretOrKey: 'Secret1234'
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
auth.module.ts
providers: [AuthService, JwtStrategy],
exports: [JwtStrategy, PassportModule],
providers: [AuthService, JwtStrategy]
exports: [JwtStrategy, PassportModule]
auth.controller.ts
@Post('/test')
test(@Req() req) {
console.log('req', req);
}
결과 ===> 실패
UseGuards
미들웨어 사용UseGuards
안에 @nestjs/passport
에서 가져온 AuthGuard()
를 이용하면 요청안에 유저 정보를 넣어줄 수 있다. @Post('/test')
@UseGuards(AuthGuard())
test(@Req() req) {
console.log('req', req);
}
Nest JS에는 여러가지 미들웨어가 있다.
(Pipes
, Filters
, Guards
, Interceptors
등)
각각의 미들웨어가 불러지는(called) 순서
middleware -> guard -> interceptor (before) -> pipe
-> controller -> service -> controller -> interceptor (after)
-> filter (if applicable) -> client
req.user
가 아닌 바로 user
라는 파라미터로 가져올 수 있다.get-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { User } from './user.entity';
export const GetUser = createParamDecorator(
(data, ctx: ExecutionContext): User => {
const req = ctx.switchToHttp().getRequest();
return req.user;
},
);
auth.controller.ts
@Post('/test')
@UseGuards(AuthGuard())
test(@GetUser() user: User) {
console.log('user', user);
}
board
모듈에서 쓸 수 있어야 하기에 boardmodule
에서 인증 모듈 imports 해오기authmodule
에서 export 하는 어떠한 것이든 boardmodule
에서 사용 가능board.module.ts
@Module({
imports: [
TypeOrmExModule.forCustomRepository([BoardRepository]),
AuthModule // 추가
],
UseGuards(AuthGuard())
를 이용해서 이 사람이 요청을 줄 때 올바른 토큰을 가지고 요청을 주는지 본 후에 게시물에 접근 할 권한을 줌
AuthGuard
는 각각의 라우트 별로 줄 수도 있고 한번에 하나의 컨트롤러 안에 들어있는 모든 라우트에 줄 수도 있다.
현재는 board
컨트롤러 안에 있는 모든 라우트에 AuthGuard
를 적용
board.controller.ts
@Controller('boards')
@UseGuards(AuthGuard()) // 추가