본문 바로가기
엘리스 AI 트랙 4기/elice AI track

[6주차] JSON Web Token

by _sweep 2022. 2. 25.

2월 25일 자 학습 내용 정리입니다.

 

 

 JWT

JWT(JSON Web Token)는 인증을 위한 정보를 전자 서명을 이용하여 확인하는 방법이다.
session과 달리 정보를 가져오거나 보낼 때 특별한 저장소를 이용하지 않는다.

 

session은 기본적으로 웹 브라우저의 통신 스펙이기 때문에 모바일 앱 등 웹 브라우저가 아닌 어플리케이션의 경우 이를 활용하기 부적합하다.
반면 JWT는 어느 클라이언트에서나 동일한 방식의 사용자 인증을 구현할 수 있다.

 

JWT는 header, payload, signature의 세 가지 구성요소로 이루어진다.

  • header : 토큰의 타입, 데이터 서명 방식
  • payload : 전달되는 데이터
  • signature : header와 payload의 전자서명

 

JWT는 이름에서도 알 수 있듯 Web Token이기 때문에 데이터를 웹에서 사용한다.
따라서 웹에서 문제없이 사용할 수 있는 문자열로만 구성된 base64 인코딩을 사용한다.

 

jwt.io

 

JWT의 payload는 단순히 정보를 base64 인코딩한다.

{ "name": kiki, "message": "hello" }를 인코딩해 JWT로 만든 결과는 위와 같다.
이를 디코딩하게되면 기존의 정보 { "name": kiki, "message": "hello" }가 그대로 드러나니 민감한 정보는 제외하고 토큰을 생성해야 한다.

 

서버는 JWT를 생성할 때 비공개 키를 이용하여 서명한다.

위 사진에서는 secret_key라는 비공개 키를 이용해 서명하고 있다.
따라서 payload를 조작하면 서명이 일치하지 않기 때문에 인증이 실패하게 된다.

 

JWT의 작동 방식은 다음과 같다.

  1. 사용자 로그인
  2. 서버는 로그인된 유저 정보를 JWT로 생성
  3. JWT를 클라이언트에 전달
  4. 클라이언트는 전달받은 JWT를 이용하여 인증이 필요한 요청에 사용

 

 

✅ JWT 만들기

JWT는 jsonwebtoken 패키지를 이용한다.

 

$ npm install jsonwebtoken
$ yarn add jsonwebtoken

 

npm을 사용하는 경우 npm install, yarn을 사용하는 경우 yarn add 명령어를 통해 해당 패키지를 내려받을 수 있다.

 

✔️ jwt.sign()

jwt.sign(payload, secretOrPrivateKey, [options, callback])

 

.sign()을 통해 JWT를 만든다.

인자로는 인코딩할 대상인 payload, 인코딩할 때 쓰일 secret key가 주어진다.

option에 아무것도 주지 않을 경우 기본 알고리즘으로 HS256이 사용된다.

 

✔️ jwt.verify()

jwt.verify(token, secretOrPublicKey, [options, callback])

 

.verify()를 이용해 JWT를 디코딩한다.

인자로는 JWT와 인코딩할 때 쓰인 secret key가 주어진다.

.verify()의 결과는 인코딩했던 payload가 원본 그대로 나타나게 되므로 payload에 민감한 정보는 쓰지 않는 것이 좋다.

 

const jwt = require('jsonwebtoken');

const token = jwt.sign({ foo: 'bar' }, 'shhhhh');
const decoded = jwt.verify(token, 'shhhhh');

console.log(decoded.foo) 

// output
// bar

 

 

 

 

JWT+Cookie

Cookie는 웹 서비스에서 사용하는 정보를 클라이언트에 저장하고 HTTP 요청 시 이를 함께 전송하여 클라이언트 정보를 서버에 전달하는 기술이다.

 


Session을 사용한 로그인 처리의 경우 Cookie에 SessionID를 저장하고 Session Store에서 유저 정보를 가져온다.
JWT를 사용한 로그인 처리는 JWT를 쿠키에 저장하고 JWT로 요청해 서명 확인 후 유저 정보를 받아 사용한다.
이는 Session을 사용했을 때보다 데이터베이스 접근이 줄어 효율적인 인증을 구현할 수 있다.

 

const { Router } = require('express');
const jwt = require('jsonwebtoken');
const passport = require('passport');

const router = Router();

router.post('/', passport.authenticate('local'), (req, res, next) => {
    const token = jwt.sign(req.user, 'secret_key');
    res.cookie('token', token);
    res.redirect('/');
});

module.exports = router;

 

jwt.sign()으로 token을 생성하고 res.cookie()를 사용해 token을 클라이언트에 쿠키로 전달할 수 있다.

 

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.verify() 대신 passport-jwt 패키지를 이용해 요청된 token의 서명을 확인하고 인증하는 기능을 구현할 수 있다.

 

 

🔍 참조

jsonwebtoken https://www.npmjs.com/package/jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback

jwt https://jwt.io/

 

댓글