μ λ² μκ°μ λ‘κ·ΈμΈ μλΉμ€λ₯Ό λ§λ€κΈ° μν΄μ MVC ν¨ν΄μ λ§μΆ°μ Controller, Service μ½λλ₯Ό μμ±ν΄μ μμ‘΄μ± μ£Όμ
κ³Ό μ€ν ν
μ€νΈκΉμ§ νλ€. κ·Έλμ μ°λ¦¬λ jwt
λ₯Ό μ΄μ©ν λ‘κ·ΈμΈ λ°©μμ ꡬνν μμ μ΄κΈ° λλ¬Έμ κ·Έμ λ§λ μ€μ μ μ΄λ² ν¬μ€ν
μμ λ€λ€λ³Ό μμ μ΄λ€.
κ·ΈλΌ passport
μ jwt
λ₯Ό μ΄μ©ν΄μ λ‘κ·ΈμΈ μΈμ¦μ νμν μ½λλ€μ ꡬνν΄λ³΄λλ‘ νμ.
μ¬μ μ€λΉλ‘ Controller, Service μ½λλ μμ±λμλ€κ³ κ°μ νκ³ μ§ννλ μ΄μ ν¬μ€ν μ μ°Έκ³ ν΄μ μ½λ μμ±κΉμ§λ§ νκ³ μ€μλ©΄ νΈνκ² λ³΄μ€ μ μμ κ² κ°μ΅λλ€.
μ λ² μκ°μ passport
λͺ¨λμ λν΄μλ μ€μΉλ₯Ό νμμΌλ, λ€μ μ²μλΆν° μ§κ³ λμ΄κ°λλ‘ νκ² μ΅λλ€. (λ§μ½ μ΄μ ν¬μ€ν
μμ μ€μΉλ₯Ό νμ
¨μΌλ©΄ νμ§ μμλ λ©λλ€!)
node
νκ²½μμ λ§μ΄ μ¬μ©νλ μΈμ¦ λ―Έλ€μ¨μ΄μΈ passport
μ jwt
λ₯Ό μ΄μ©νκΈ° μν΄μλ ν¨ν€μ§λ₯Ό μ€μΉν΄μ£Όμ΄μΌ νλλ° ν°λ―Έλμμ νλ‘μ νΈ λ£¨νΈ λλ ν λ¦¬λ‘ μ§μ
νκ³ λ€μ λͺ
λ Ήμ΄λ₯Ό μ
λ ₯ν΄μ€λ€.
$ npm install --save @nestjs/passport passport passport-local
$ npm install --save-dev @types/passport-local
$ npm install --save @nestjs/jwt passport-jwt
$ npm install --save-dev @types/passport-jwt
κ·Έ λ€μμ passport
μ jwt
μ€μ μ νκΈ° μν μ½λλ₯Ό μμ±ν΄λ³΄λλ‘ νκ² λ€.
λ¨Όμ , jwt
λ₯Ό μ¬μ©νκΈ° μν΄μ guard
, payload
, strategy
3κ°μ§λ₯Ό μ€μ ν΄μ£Όμ΄μΌ νλ€. λ°λΌμ auth
ν΄λ λ°μ jwt
ν΄λλ₯Ό μμ±νκ³ 3κ°μ§μ λν μ½λλ₯Ό μμ±ν΄μ€λ€.
jwt.guard.ts
jwt.payload.ts
jwt.strategy.ts
μμ±μ΄ μλ£λμμΌλ©΄ νλμ© μμ±νλ©΄μ μ΄λ€ κΈ°λ₯μΈμ§ μμκ°λ³΄λλ‘ νμ.
μ½λλ₯Ό μμ±νκΈ°μ μ μ°μ guard
μ λν΄μ μ§μ΄λ³Ό νμκ° μμ κ² κ°λ€. nest
곡μλ¬Έμ μ€ FAQ ννΈμ Request lifecycle μ΄λΌλ ννΈκ° μλ€. ν΄λΉ ννΈμ λ°λ₯΄λ©΄ μμ²μ μ½λ νλ¦μ λ€μκ³Ό κ°λ€.
4, 5, 6λ²μ 보면 Guard
λΌλ λΆλΆμ΄ μλλ°, μ°λ¦¬κ° νμ¬ μμ±νλ €κ³ νλ Guard
μ κ°μ μΉκ΅¬μ΄λ€. λ°λΌμ Guard
λ₯Ό ν΅κ³Όν΄μΌμ§ 본격μ μΌλ‘ μμ²μ μ²λ¦¬κ° μμλλ 컨νΈλ‘€λ¬μ λλ¬ ν μ μκ² λλ€.
κ·Έλ¬λ©΄ Guard
λ 무μμΈκ°? λ¨μΌ μ±
μμΌλ‘ λ°νμμ μ‘΄μ¬νλ νΉμ 쑰건 (κΆν, μν λ±)μ λ°λΌμ μ§μ λ μμ²μ λΌμ°ν° νΈλ€λ¬μ μν΄ μ²λ¦¬ν μ§ μ¬λΆλ₯Ό κ²°μ νλ€.
- λ¨μΌμ± μ : ν ν΄λμ€λ νλμ μ± μμ κ°μ§λ κ²μΌλ‘ κ°μ²΄μ§ν₯ 5μμΉμ SRPμ ν΄λΉ
(μ¬κΈ°μ λ¨μΌμ± μμ μμ²μ μΉμΈν μ§ λ§μ§ νλλ§ κ°μ§λ ν΄λμ€λΌλ μλ―Έμ΄λ€.)
νμ§λ§ Guard
λ³΄λ€ λ¨Όμ μ€ν μμλ₯Ό κ°μ§λ λ―Έλ€μ¨μ΄μμ ν΄μ£Όλ©΄ λλλ° κ΅³μ΄ μ Guard
λ₯Ό λ λ§λ€μμκΉ? λΌκ³ μλ¬Έμ κ°μ§ μ μμ κ² κ°λ€. μ΄λ° μλ¬Έμ 곡μλ¬Έμμμ μμ£Ό λ§λνκ² ν΄κ²°ν΄μ£Όμλλ° μ΄μ λ λ€μκ³Ό κ°λ€.
- λ―Έλ€μ¨μ΄λ λ³Έμ§μ μΌλ‘ λ©μ²ν΄μ,
next()
ν¨μλ₯Ό νΈμΆν ν μ΄λ€ νΈλ€λ¬κ° μ€νλλμ§ μ μ μλ€. (λ°λλ‘ κ°λλ λ€μμ μ€νλ μμ μ λͺ ννκ² μκ³ μλ€. λ°λΌμ μ νν μ§μ μμ μ²λ¦¬ λ‘μ§μ λ£μ μ μκ³ , μνν μ μλλ‘ νλ€.)- μλ¬Έμ ν΄λΉ λ§ν¬λ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ.
λν Guard
λ κΈλ‘λ², 컨νΈλ‘€λ¬, λΌμ°νΈλ‘ λλλλ° μ΄λ Guard
λ₯Ό κ°λ°μκ° μ΄λμ μ€μ νλλμ λ°λΌμ κΈλ‘λ²λ‘ μλνκΈ°λ νκ³ , 컨νΈλ‘€λ¬μμλ§ μλνκΈ°λ νκ² λλ€.
μ΄μ Guard
μ λν΄μ κ°λ΅νκ² μμ보μμΌλ μ½λλ₯Ό μμ±ν΄λ³΄λλ‘ νμ. μ½λμ λ΄μ©μ λ€μκ³Ό κ°λ€.
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
AuthGuard
:Guard
μ μ λ΅μ μ€νμμΌμ£Όλ ν¨μκ° λ΄μ₯λ λΌμ΄λΈλ¬λ¦¬λ‘Guard
κ° μ€νλ λjwt.strategy.ts
μ½λλ₯Ό μ€νμμΌμGuard
μ νμν λ‘μ§μ΄ μ€νλ μ μλλ‘ λμμ€λ€. (μ΄λ μ λ΅μjwt
λ°©μμ μ΄μ©ν΄μ μμ²μΌλ‘λΆν°Guard
λ₯Ό νλ€.)
jwt.strategy.ts
μ½λλ Guard
μ μ λ΅μ λ΄μ μ½λλ‘ Strategy
λ΄λΆμ validate
ν¨μκ° μ€νλλ©΄μ μΈμ¦ μ μ°¨λ₯Ό κ±°μΉκ² λλ€.
μ½λλ λ€μκ³Ό κ°μ΄ μμ±ν μ μλ€.
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { Payload } from './jwt.payload'; // λ€μμμ 곧μ₯ μμ±ν μμ
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
// ν€λ Authentication μμ Bearer ν ν°μΌλ‘λΆν° jwtλ₯Ό μΆμΆνκ² λ€λ μλ―Έ
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'secret', // jwt μμ±μ λΉλ°ν€λ‘ μ¬μ©ν ν
μ€νΈ (λ
ΈμΆ X)
ignoreExpiration: false, // jwt λ§λ£λ₯Ό 무μν κ²μΈμ§ (κΈ°λ³Έκ°: false)
});
}
async validate(payload: Payload) {
const user = payload.sub === '0'
if (user) {
return user; // request.userμ ν΄λΉ λ΄μ©μ λ£μ΄μ€λ€ (Passport λΌμ΄λΈλ¬λ¦¬κ° ν΄μ€)
} else {
throw new UnauthorizedException('μ κ·Ό μ€λ₯');
}
}
}
μ°μ μ μ΄λ κ² μ κ³ λμ΄κ° μ μλλ‘ νμ.
payload
λ jwt
μμ μ¬μ©μκ° μνλ μ 보λ₯Ό λ£λ λΆλΆμΌλ‘ νμ
μ μ μνλ μ½λμ΄λ€. λ°λΌμ μ¬μ©μκ° λ³΄μμ μνμ΄ κ°μ§ μλ μ μμ μνλ μ 보λ₯Ό λ£μ΄μ μ¬μ©νλ©΄ λλλ° μ°μ μ μλμ κ°μ΄ μμ±ν΄μ μ¬μ©ν΄λ³΄λλ‘ νμ.
export type Payload = {
email: string;
sub: string;
};
μ¬κΈ°κΉμ§ μλ£νμΌλ©΄ λ‘κ·ΈμΈ ν μΈμ¦μ λν΄μ μ¬μ©ν μ μλ μ½λλ€μ λ§λ¬΄λ¦¬κ° λμλ€.
λ§μ§λ§μΌλ‘ λ‘κ·ΈμΈ νμ λ jwt
ν ν°μ λ§λ€κ³ , ν΄λΉ ν ν°μΌλ‘ μΈμ¦μ΄ λλμ§ ν λ² νμΈν΄λ³΄λλ‘ νμ.
μ΄μ λλμ΄ μ λ² μκ°μ λ§λ€μλ λ‘κ·ΈμΈ μ»¨νΈλ‘€λ¬, μλΉμ€λ₯Ό μ¬μ©ν λκ° μλ€.
λ¨Όμ λ‘κ·ΈμΈ μ»¨νΈλ‘€λ¬λ₯Ό μΌκ³ λ€μκ³Ό κ°μ΄ μ½λ©μ ν΄λ³΄λλ‘ νμ.
import { Controller, Post, Req } from '@nestjs/common';
import { LoginService } from './login.service';
import { Request } from 'express';
@Controller('login')
export class LoginController {
constructor(private readonly loginService: LoginService) {}
@Post()
login(@Req() request: Request) {
const { email, password } = request.body;
return this.loginService.login(email, password);
}
}
κ·Έλ¦¬κ³ λ‘κ·ΈμΈ μλΉμ€ μ½λμμλ λ€μκ³Ό κ°μ΄ μμ±ν μ μλλ‘ νλ€.
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class LoginService {
constructor(private jwtService: JwtService) {}
login(email: string, password: string) {
if (email === 'test@test.com' && password === 'test') {
const payload = { email: email, sub: '0' };
return this.jwtService.sign(payload);
}
throw new UnauthorizedException('μΈμ¦λμ§ μμ μ¬μ©μμ
λλ€.');
}
}
κ·Έλ¦¬κ³ μ§κΈ μ°λ¦¬κ° Passport, jwt κΈ°λ₯μ λ‘κ·ΈμΈ μ»¨νΈλ‘€λ¬, μλΉμ€μμ μ΄μ©ν΄μ£ΌκΈ° μν΄μλ μμ‘΄μ± μ£Όμ μ΄ λμ΄μΌ νλλ° μ΄λ λ‘κ·ΈμΈ λͺ¨λ μ½λμμ λͺ μν΄μ£Όλ©΄ λλ€. μ½λλ λ€μκ³Ό κ°λ€.
import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';
import { LoginService } from './login.service';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from '../jwt/jwt.strategy';
@Module({
imports: [
// sessionμ μ¬μ©νμ§ μμ μμ μ΄κΈ° λλ¬Έμ false
PassportModule.register({ defaultStrategy: 'jwt', session: false }),
// jwt μμ±ν λ μ¬μ©ν μν¬λ¦Ώ ν€μ λ§λ£μΌμ μ μ΄μ£ΌκΈ°
JwtModule.register({
secret: 'secret',
signOptions: { expiresIn: '1y' },
}),
],
controllers: [LoginController],
providers: [LoginService, JwtStrategy], // strategy μμ‘΄μ± μ£Όμ
μ μν΄μ
})
export class LoginModule {}
κ·Έλ¦¬κ³ ν¬μ€νΈλ§¨μ μ΄μ©ν΄μ λ‘κ·ΈμΈμ μλνλ©΄ λ€μκ³Ό κ°μ΄ JWT
ν ν°μ 리ν΄ν΄μ€λ€. λ°λΌμ 리ν΄λ ν ν°μ κ°μ§κ³ μμΌλ‘ μΈμ¦μ μ§ννλ©΄ λλ€.
μ§κΈ λ°κΈλ ν ν°μ μ΄λμ μ 볡μ¬ν΄λμλ€κ° μ΄λ² μΉμ μμ μ¬μ©ν μ μλλ‘ νμ.
μ΄μ λ§μ§λ§μΌλ‘ μκΉ μ΄μ¬ν μ μν jwt
κ΄λ ¨ μ½λλ€μ μ μ©ν΄λ³΄λ μκ°μ κ°μ§λλ‘ νμ.
κ·Έλ¬κΈ° μν΄μλ κ°λ¨ν 컨νΈλ‘€λ¬ νλλ₯Ό μ μν΄λ³΄μ.
src
ν΄λ λ°μ user
ν΄λλ₯Ό λ§λ€κ³ user.controller.ts
μ½λλ₯Ό λ§λ€μ΄λ³΄λλ‘ νμ.
// user.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../jwt/jwt.guard';
@Controller('user')
export class userController {
@UseGuards(JwtAuthGuard)
@Get('all')
getAllUser() {
return {
success: true,
user: {
email: 'test@test.com',
},
};
}
}
μμ μ½λλ JwtAuthGuard
λ₯Ό ν΅κ³Όνλ©΄ μ±κ³΅μ΄λΌλ λ©μμ§μ ν¨κ» μ μ μ΄λ©μΌμ 보μ¬μ£Όλ μμΌλ‘ μμ£Ό κ°λ¨νκ² λ§λ€μλ€. μμ κ°μ λ°©μμΌλ‘ @UseGuards
μ λ
Έν
μ΄μ
μ μ΄μ©ν΄μ κΈλ‘λ², 컨νΈλ‘€λ¬, λΌμ°νΈμ μμ μ΄ μνλ κ³³μ΄ μΈμ¦μ λΆμ°©ν μ μλ€.
κ·ΈλΌ ν¬μ€νΈλ§¨μΌλ‘ ν μ€νΈλ₯Ό ν΄λ΄μΌνλλ° ν μ€νΈλ λ€μκ³Ό κ°μ΄ νλ€.
- λ¨Όμ GET http://localhost:ν¬νΈ/user/all λ‘
URL
μ λ³κ²½Headers
νμΌλ‘ μ΄λ ν λ€μμ λ΄μ©μ μ λλ€.
- KEY: Authentication
- VALUE: Bearer <ν ν°>
(Header
μAuthentication
ν€μBearer
ν ν°μ λ£λ μ΄μ λjwtStrategy
μ½λμμjwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
μΌλ‘ λͺ μν΄μ£ΌμκΈ° λλ¬Έμ΄λ€.)
- Sendλ₯Ό λλ¬μ 리ν΄κ°μ νμΈνλ€.
λ°λΌμ μ±κ³΅νκ² λλ©΄ λ€μκ³Ό κ°μ΄ κ²°κ³Όκ° λ¬λ€.
λ§μ½ μ€ν¨νλ©΄ λ€μκ³Ό κ°μ΄ κ²°κ³Όκ° λ¬λ€. (Bearer 111<ν ν°> μΌλ‘ μ΄μνκ² μμ )
μ°λ¦¬λ μΈμ μ μ¬μ©νμ§ μκ³
jwt
ν ν°μ μ΄μ©ν΄μ μΈμ¦μ ꡬννμλλ°, μμ¦ μ΄μ νκ²½μμ νλμ μλ²λ§ μ¬μ©νλ κ²μ΄ μλκ³ μ¬λ¬ κ°μ μλ²λ₯Ό λμμ λλ¦¬κ² λλ€.
κ·Έλ¦¬κ³ μΈμ μ μλ²μ μ’ μλκΈ° λλ¬Έμ κ°κ° μλ²λ§λ€ λ΄κ° μμ±ν μΈμ μ΄ λκΈ°νκ° λμ΄μΌμ§ λ‘λλ°Έλ°μλ‘ λ€λ₯Έ μλ²λ‘ μ΄λλλλΌλ 컨ν μ€νΈλ₯Ό μμ§ μκ³ μμ ν μ μλ€.
μ΄λ° λΆλΆμ΄ λ²κ±°λ‘μμjwt
ν ν°μ μ΄μ©ν΄μ μΈμ¦μ ꡬννλ κ²μ΄κ³ μ΄ λ°©λ²μ μ¬μ©νλ©΄ μλ²λ§λ€ μΈμ μ μ¬μ©ν νμκ° μκ² λλ€.
λκ° λ²Όλ½μ²λΌ Passport
μ jwt
λ₯Ό μ΄μ©ν λ‘κ·ΈμΈ κ΅¬νμ΄ μ§λκ°κ² λμλλ°, μ¬μ€ μμ§ μμ°μΉ μμ λΆλΆλ€μ΄ μκ³ μλ²½νκ² μ΄ν΄λ κ²μ΄ μλλ€. λ°λΌμ κ³μ 곡λΆλ₯Ό ν μμ μ΄κ³ μλ‘κ² μ μ¬μ€μ΄ μμΌλ©΄ μ€κ° μ€κ° ν¬μ€ν
λ΄μ©μ μμ νκ±°λ μΆκ°ν μ μλλ‘ νκ² μ΅λλ€.
κ·Έλ¦¬κ³ μ§κΈμ νλμ½λ©μΌλ‘ κ°μ λΉκ΅νκ³ μ²λ¦¬νμλλ°, λ€μ ν¬μ€ν
μλ λ°μ΄ν°λ² μ΄μ€λ₯Ό μ°λνκ³ nest
κΈ°λ₯μ μ’ λ μ κ·Ήμ μΌλ‘ νμ© ν μ μλ λ°©μμ μμ±ν΄λ³΄λλ‘ νκ² μ΅λλ€.
κΈ΄κΈ λ΄μ£Όμ μ κ°μ¬ν©λλ€.
κ·ΈλΌ μ΄λ§ μ΄μ΄..ππ»ββοΈ
λ무 μ’μμ