2월 25일 자 학습 내용 정리입니다.
✅ JWT
JWT(JSON Web Token)는 인증을 위한 정보를 전자 서명을 이용하여 확인하는 방법이다.
session과 달리 정보를 가져오거나 보낼 때 특별한 저장소를 이용하지 않는다.
session은 기본적으로 웹 브라우저의 통신 스펙이기 때문에 모바일 앱 등 웹 브라우저가 아닌 어플리케이션의 경우 이를 활용하기 부적합하다.
반면 JWT는 어느 클라이언트에서나 동일한 방식의 사용자 인증을 구현할 수 있다.
JWT는 header, payload, signature의 세 가지 구성요소로 이루어진다.
- header : 토큰의 타입, 데이터 서명 방식
- payload : 전달되는 데이터
- signature : header와 payload의 전자서명
JWT는 이름에서도 알 수 있듯 Web Token이기 때문에 데이터를 웹에서 사용한다.
따라서 웹에서 문제없이 사용할 수 있는 문자열로만 구성된 base64 인코딩을 사용한다.
JWT의 payload는 단순히 정보를 base64 인코딩한다.
{ "name": kiki, "message": "hello" }를 인코딩해 JWT로 만든 결과는 위와 같다.
이를 디코딩하게되면 기존의 정보 { "name": kiki, "message": "hello" }가 그대로 드러나니 민감한 정보는 제외하고 토큰을 생성해야 한다.
서버는 JWT를 생성할 때 비공개 키를 이용하여 서명한다.
위 사진에서는 secret_key라는 비공개 키를 이용해 서명하고 있다.
따라서 payload를 조작하면 서명이 일치하지 않기 때문에 인증이 실패하게 된다.
JWT의 작동 방식은 다음과 같다.
- 사용자 로그인
- 서버는 로그인된 유저 정보를 JWT로 생성
- JWT를 클라이언트에 전달
- 클라이언트는 전달받은 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/
'엘리스 AI 트랙 4기 > elice AI track' 카테고리의 다른 글
[6주차] 3계층 설계 (0) | 2022.02.26 |
---|---|
[6주차] OAuth와 Nginx (0) | 2022.02.25 |
[6주차] Session과 Session Store (0) | 2022.02.23 |
[6주차] Passport.js와 로그인 구현하기 (0) | 2022.02.23 |
[6주차] crypto와 회원가입 구현하기 (0) | 2022.02.23 |
댓글