JWT의 대한 정보는 엄청많은데 왜 sessison에 대한 정보는 이리 적은 것입니까?
그래서 열심히 구글링하고 문서를 파면서 방법을 찾아냈습니다 :)
Client로부터 오는 일련의 요청을 하나의 상태로 보고 그 상태를 일정하게 유지하는 기술
클라이언트가 웹 서버에 접속해있는 상태가 하나의 단위
세션은 웹서버에 웹 컨테이너의 상태를 유지하기 위한 정보를 저장합니다. 브라우저를 닫거나 서버에서 세션을 삭제하면 세션이 삭제됩니다. 세션은 각 클라이언트의 고유세션 ID를 부여하는데, 이것으로 클라이언트를 구분하여 각 클라이언트의 요구에 맞는 응답을 반환합니다.
main.ts에는 express에서 session을 설정을 해주는 거와 같이 같은방식으로 선언해주면 된다.
/main.ts
...
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const sessionMiddleware = session({
secret: process.env.SECRET, // 세션을 암호화하기 위한 암호기 설정
resave: false, // 모든 request마다 기존에 있던 session에 아무런 변경 사항이 없을 시에도 그 session을 다시 저장하는 옵션
// saveUnitialized: 초기화되지 않은 세션을 저장할지 여부를 나타낸다.
saveUninitialized: false,
// 세션 쿠키에 대한 설정을 나타낸다.
cookie: {
maxAge: 60000 * 60, // 1 hour
httpOnly: true,
},
});
app.use(sessionMiddleware);
// Passport를 초기화하는 미들웨어, 이를 통해 Passport의 인증/인가를 사용할 수 있다.
app.use(passport.initialize());
// Passport 세션을 사용하기 위한 미들웨어이다. 이를 통해 Passport는 세션을 기반으로 사용자의 인증 상태를 유지 관리 할 수 있다.
app.use(passport.session());
await app.listen(3000);
}
bootstrap();
startegy를 통해 로그인에서 로그인한 정보를 validate를 통해 naver 회원 정보를 넘긴다.
/src/auth/naver-strategy.ts
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver';
import { Injectable } from '@nestjs/common';
import { AuthService } from 'src/auth/auth.service';
@Injectable()
export class NaverStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
clientID: process.env.NAVER_CLIENT_ID,
clientSecret: process.env.NAVER_CLIENT_SECRET,
callbackURL: process.env.NAVER_CALLBACK_URL,
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: any,
): Promise<any> {
const id = profile._json.id;
const provider = 'naver';
const name = profile._json.nickname;
const image = profile._json.profile_image;
const user = {
id,
name,
image,
provider,
};
return done(null, user);
}
}
controller에 있는 NaverAuthGuard를 넘어가고 네이버 로그인이 성공하면 naver/callback 으로 넘어간다.
/src/auth/auth.controller.ts
export class AuthController {
constructor(private authService: AuthService) {}
@Get('naver')
@UseGuards(NaverAuthGuard)
async naverLogin(): Promise<void> {}
@Get('naver/callback')
@UseGuards(NaverAuthGuard)
async naverLoginCallback(@Req() req, @Res() res): Promise<void> {
const user = req.user;
await this.authService.OAuthLogin(user);
return res.redirect('/list.html');
}
jwt토큰 방식에서는 단순하게 코드를 작성할 수 있지만
controller에 접근하였을 때, canActivate에서 request정보를가져와 super.logIn을 통해 로그인을 연결해준다.
/src/auth/naver-auth.guard
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class NaverAuthGuard extends AuthGuard('naver') {
constructor() {
super();
}
async canActivate(context: ExecutionContext) {
const activate = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return activate;
}
}
serialize를 거치고
deserialize를 통해 req.user
을 불러올 수 있게된다.
/serializeUser
import { PassportSerializer } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { User } from './user/entities/user.entity';
import { UserService } from './user/user.service';
@Injectable()
export class SessionSerializer extends PassportSerializer {
constructor(private readonly userService: UserService) {
super();
}
async serializeUser(
user: User,
done: (err: any, user?: any) => void,
): Promise<any> {
//console.log(user, 'serializeUser'); // 테스트 시 확인
done(null, user);
}
async deserializeUser(
payload: any,
done: (err: any, user?: any) => void,
): Promise<any> {
const response = await this.userService.findById(payload.id);
// console.log(user, 'deserializeUser'); // 테스트 시 확인
const user = JSON.parse(JSON.stringify(response));
delete user.password;
return user ? done(null, user) : done(null, null);
}
}
local의 경우 usernamefield와 passwordField를 꼭 선언해줘야하니 잊지 않도록하자 저기서 말하는 field가 client에서 form tag의 name속성을 말한다.
validate에서는 db에 저장되있는지 확인하기위한 직접만든 함수이다.
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { Strategy } from 'passport-local';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
usernameField: 'email',
passwordField: 'password',
});
}
async validate(email: string, password: string): Promise<any> {
const user = {
id: email,
password: password,
provider: 'local',
};
return await this.authService.LocalLogin(user);
}
}
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalGuard extends AuthGuard('local') {
constructor() {
super();
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const result: boolean = (await super.canActivate(context)) as boolean;
await super.logIn(context.switchToHttp().getRequest());
return result;
}
}