JWT(JSON Wen Token)
백엔드 개발자라면 지구 끝까지 따라와 묻게 되는 용어 JWT
알고나면 별로 어려운 용어는 아니지만 처음에 배울 때는 많이 힘들게 하는 친구중 하나입니다. 해당 글에서는 JWT
에 대한 내용을 별도로 다루고 있지 않습니다. 그러므로 JWT
에 대한 내용을 공부하시고자 한다면 아래 글을 참고 해주시기 바랍니다.
Token 발급 하기
발급을 해주기 위해서는 기본적인 jwt
설정이 필요로 합니다. /src/auth/auth.module.ts
에 모듈을 추가 해줍니다.
imports: [
UserModule,
PassportModule,
JwtModule.register({
global: true,
secret: 'tempSecretKey',
signOptions: { expiresIn: '1d' },
}),
],
옵션 | 설명 |
global |
전역으로 jwt를 설정하겠다는 옵션 |
secret |
jwt를 암호화시에 사용되는 secrey key |
signOptions |
expired 기간을 지정하는 옵션 |
기본적인 로직은 회원가입이 완료된 회원이라는 전제하에 설명하도록 하겠습니다. 일단 회원가입이 마친 유저를 DB
에서 찾아 Password
를 검증하는 과정을 거치게 됩니다. 검증을 성공적으로 마치게 된다면 새로운 AccessToken
을 발급을 해줍니다.
@Injectable()
export class AuthService {
constructor(private userService: UserService, private jwtService: JwtService) {}
async signin(email: string, password: string) {
const user = await this.userService.findOneByEmail(email);
if (!user) throw new UnauthorizedException();
const isMatch = password === user.password;
if (!isMatch) throw new UnauthorizedException();
return {
accessToken: this.jwtService.sign({
sub: user.id,
}),
};
}
}
여기서 this.jwtService
라는 메소드를 사용하게 되는데 이전에 설정한 jwtModule
설정 값이 주입 받아서 사용되어지고 있습니다.
이렇게 만들어진 토큰은 로그인을 요청한 유저에게 보내지게 됩니다.
토큰을 발급하는 것을 어렵지 않게 구현해나갈 수 있는 부분입니다.
Token 인증하기
Token을 발급 하였으니 이제는 인증해야하는 과정을 구현해나가야 합니다. 인증 과정은 발급 과정보다 복잡하니 차근차근 읽어 보시기 바랍니다.
먼저 /src/auth/jwt-auth.guard.ts
를 작성해줍니다.
// jwt-auth.auard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
return super.canActivate(context);
}
}
guard
의 경우 하나의 검증 미들웨어라는 용어로 사용되고 있습니다. 여기서 jwt-guard의 경우 private
한 엔드포인트와 public
한 엔드포인트를 구분하는 하나의 미들웨어로 보시면 되겠습니다. 매개변수 중 IS_PUBLIC_KEY
라는 변수를 받고 있는데 해당 값이 true
라면 jwt
검증없이 사용가능한 엔드포인트를 제공하고 있다고 보시면 되겠습니다.
코드 | 설명 |
extends AuthGuard('jwt') |
AuthGuard는 @nestjs/passport 패키지에서 제공하는 클래스입니다. 이를 상속받아 jwt 전략을 사용하도록 설정합니다. |
constructor(private reflector: Reflector) |
Reflector는 Nest.js에서 제공하는 유틸리티 클래스로, 데코레이터에 대한 메타데이터를 추출할 때 사용됩니다. 이 클래스의 생성자에서는 Reflector를 의존성으로 주입받고 있습니다. |
canActivate(context: ExecutionContext) |
AuthGuard 클래스에서 정의한 canActivate 메서드를 오버라이딩합니다. |
this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [context.getHandler(), context.getClass()]); |
Reflector를 사용해 현재 요청의 메타데이터를 추출하고, 그 요청이 Public인지 여부를 판단합니다. |
if (isPublic) return true; |
만약 요청이 Public이라고 표시되어 있다면, 인증 검사 없이 접근을 허용합니다. |
return super.canActivate(context); |
그렇지 않다면, 부모 클래스(AuthGuard)의 canActivate 메서드를 호출하여 일반적인 JWT 인증을 수행합니다. |
이제는 이러한 전략을 사용할 수 있도록 데코레이터를 작성해보도록 하겠습니다. /src/common/decorator/public.decorator.ts
파일을 생성하고 아래 코드를 작성하겠습니다.
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
이제 이렇게 생성한 데코레이터를 컨트롤러에 사용해주면 검증없이 통과되는 public
엔드포인트가 설정이 된 것입니다.
@Public()
@Post('signin')
async signin(@Body() { email, password }: SigninReqDto) {
return this.authService.signin(email, password);
}
이번에는 jwt
를 검증하는 /src/auth/jwt.strategy.ts
를 작성해주도록 하겠습니다.
// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'tempSecretKey',
});
}
async validate(payload: any) {
return { id: payload.sub };
}
}
각 과정에 따른 설명은 아래와 같습니다.
코드 | 설명 |
class JwtStrategy extends PassportStrategy(Strategy) |
jwt을 활용하는 인증처리를 구현하기 위해 passport 라이브러리를 상속 받아 구현하는 클래스라는 것을 말해줍니다. |
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() |
header에 authentication bearer가 포함 되어져야 하는 전략을 구현. |
ignoreExpiration: false |
expiretime을 인증 한다면 false를 아니면 true |
secretOrKey: 'tempSecretKey' |
암호화시 사용된 secretKey를 복호화시 사용하게 함. |
async validate(payload: any) |
모든 검증이 완료가 되면 반환되는 값을 만들어 주는 메소드 |
각 내용들이 이름이 길어서 어려워 보이지만 각 요소들을 자세하게 보면 그나마 쉽게 이해 할 수 있습니다. 이렇게 인증이 완료된 토큰은 쉽게 꺼내서 사용 할 수 있게 Decorator
를 작성 해줍니다.
아래의 코드는 요청시 검증이 완료된 토큰에서 반환된 유저의 ID
넘버를 Decorator
로 작성한 코드 입니다. 아래 UserAfterAuth
는 ID
값이 String
인 것을 증명하기 위한 타입 지정용 interface
입니다.
export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
});
export interface UserAfterAuth {
id: string;
}
마지막으로 컨트롤러에서 ID를 꺼내어서 사용하면 됩니다.
@ApiBearerAuth()
@ApiGetItemsResponse(FindUserResDto)
@Get()
@UseGuards(JwtAuthGuard)
findAll(@Query() { page, size }: PageReqDto, @User() user: UserAfterAuth) {
console.log(user);
return this.userService.findAll();
}
'Backend > Nest.js' 카테고리의 다른 글
Nest.js Provider (0) | 2023.09.26 |
---|---|
Nest.js Swagger Bearer 인증 적용 (1) | 2023.09.26 |
Nest.js Swagger 설치 및 설정 (0) | 2023.09.25 |
Nest.js Swagger 용 Decorator 작성 (0) | 2023.09.25 |
Nest.js class-validation, class-transformer 적용 및 사용 (0) | 2023.09.25 |