JWT?
- JSON Web Token
- 인증을 위한 정보를 특별한 저장소를 이용하지 않고, 전자 서명을 이용하여 확인하는 방법
구성
header - 토큰의 타입 (jwt), 데이터 서명 방식
payload - 전달되는 데이터
signature - 헤더와 페이로드의 전자서명
- JWT 는 Web Token, 데이터를 웹에서 사용하기 위한 스펙이므로 웹에서 문제없이 사용할 수 있는 문자열로만 구성된 base64 인코딩을 사용
보안
- JWT의 payload는 단순히 정보를 base64 encode → decode 시 정보 노출됨 → 민감한 정보는 제외하고 토큰 생성 필수
- 서버는 JWT를 생성할 때, 비공개키를 이용하여 서명을 함
- payload를 조작할 경우 서명이 일치하지 않기 때문에 인증 실패
작동 방식
- 사용자 로그인
- 서버는 로그인된 유저 정보를 JWT로 생성하여 클라이언트에 전달
- 클라이언트는 전달받은 JWT를 이용하여 인증이 필요한 요청에 사용
왜 사용?
- session은 기본적으로 웹 브라우저의 통신 스펙
- 모바일 앱 등, 웹 브라우저가 아닌 어플리케이션의 경우 이를 활용하기 부적합함
- JWT를 사용하면 어느 클라이언트에서나 동일한 방식의 사용자 인증을 구현 가능
사용 방법
JWT + Cookie
Cookie?
- 웹 서비스에서 사용하는 정보를 클라이언트에 저장하고, HTTP 요청 시 이를 함께 전송하여, 클라이언트 정보를 서버에 전달하는 기술
세션이랑 뭐가 다름?
- Session
- 클라이언트 정보를 서버 측 저장소에 저장하고 사용
- Cookie
- 클라이언트 정보를 클라이언트 (브라우저)에 저장하고 사용
로직 차이
- Session을 사용한 유저 로그인의 경우
- Cookie에 Session ID 저장 → Session Store에서 유저 정보 가져오기
- JWT를 쿠키에 저장하는 경우
- JWT로 요청 → 서명 확인 후 유저 정보 사용
- 데이터베이스 접근이 줄어서 효율적인 인증 구현 가능
사용 방법
- 기존 세션으로 구현된 로그인을 비활성화
- 로그인 로직에서 JWT 생성 후 쿠키로 전달
- passport-jwt 패키지로 JWT 로그인 미들웨어 작성 및 사용
로그인 로직에서 JWT 토큰 생성
- res.cookie 함수 사용하여 token을 클라이언트에 쿠키로 전달
setUserToken = (res, user) => {
const token = jwt.sign(user, secret);
res.cookie('token', token);
}
---
router.post('/', passport.authenticate('local'),
(req, res, next) => {
setUserToken(res, req.user);
res.redirect('/');
});
passport-jwt 사용하기
- passport-jwt 패키지를 이용해 요청된 JWT 토큰의 서명을 확인하고 인증하는 기능을 구현
const JwtStrategy = require('passport-jwt').Strategy;
const cookieExtractor = (req) => {
const { token } = req.cookies;
return token;
};
const opts = {
secretOrKey: secret,
jwtFromRequest: cookieExtractor, }
module.exports = new JwtStrategy(opts, (user, done) => {
done(null, user);
});
---
passport.use(jwt);
JWT 미들웨어
- JWT 토큰은 기본적으로 모든 요청에 포함
- 요청에 토큰이 있는 경우 로그인된 상태로 처리하기 위해 모든 요청에 공통적으로 적용할 수 있는 미들웨어로 JWT 로그인을 추가
app.use((req, res, next) => {
if (!req.cookies.token) {
next();
return; }
return passport.authenticate('jwt')(req,
res, next);
});
로그아웃
- 로그아웃은 간단하게 클라이언트 쿠키를 삭제하여 처리 가능
- token 값을 null로 전달하는 것과 함께, cookie의 만료 시간을 0으로 설정하여 클라이언트가 쿠키를 바로 만료시키도록 전달
res.cookie('token', null, {
maxAge: 0,
});