[ Node.js ์ˆ™๋ จ์ฃผ์ฐจ1 (1-0)] JWT(Json Web Token)

2023. 9. 11. 14:32ใ†3. Node.js

JWT(Json Web Token)

 

 

JWT(Json Web Token)์€ ์›น ํ‘œ์ค€์œผ๋กœ์จ, ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด์—์„œ ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด ๋„์›€์„ ์ฃผ๋Š” ์›น ํ† ํฐ(Web Token)์ด๋‹ค.

 

 

JSON ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๊ณ  ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณต.

์ธํ„ฐ๋„ท ํ‘œ์ค€์œผ๋กœ์„œ ์ž๋ฆฌ ์žก์€ ๊ทœ๊ฒฉ์ด๋‹ค.

๋‹ค์–‘ํ•œ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด, ์‹ ๋ขฐ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

header/ payload/ signature์˜ ํ˜•์‹์œผ๋กœ 3๊ฐ€์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ๋‹ค. (๊ฐœ๋ฏธ์ฒ˜๋Ÿผ ๋จธ๋ฆฌ, ๊ฐ€์Šด, ๋ฐฐ)

 

 

์ถ”๊ฐ€๋กœ ํŠน์ง•์„ ๋” ์•Œ์•„๋ณด์ž๋ฉด,

 

1) JWT๋Š” ๋น„๋ฐ€ ํ‚ค๋ฅผ ๋ชจ๋ฅด๋”๋ผ๋„ ๋ณตํ˜ธํ™”(Decode)๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

JWT๋ฅผ ๊ฐ€์ง„ ์‚ฌ๋žŒ์ด๋ผ๋ฉด ๋ˆ„๊ตฌ๋‚˜ ํ•ด๋‹น ํ† ํฐ์— ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ด๊ฒจ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณ€์กฐ๋งŒ ๋ถˆ๊ฐ€๋Šฅ ํ•  ๋ฟ, ๋ˆ„๊ตฌ๋‚˜ ๋ณตํ˜ธํ™”ํ•˜์—ฌ ๋ณด๋Š” ๊ฒƒ์€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค.

 

2) ๋ฏผ๊ฐํ•œ ์ •๋ณด(๊ฐœ์ธ์ •๋ณด, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ)๋Š” ๋‹ด์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.

JWT์˜ ํŽ˜์ด๋กœ๋“œ๋Š” ๋ˆ„๊ตฌ๋‚˜ ๋ณตํ˜ธํ™”ํ•˜์—ฌ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

3) JavaScript์™€ ๊ฐ™์ด ํŠน์ • ์–ธ์–ด์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

JWT๋Š” ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ ํ˜•์‹์ผ ๋ฟ, ๋‹จ์ง€ ๊ฐœ๋…์œผ๋กœ์„œ ์กด์žฌํ•˜๊ณ , ์ด ๊ฐœ๋…์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜์—ฌ ๊ณต๊ฐœ๋œ ์ฝ”๋“œ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ผ๋ฐ˜์ ์ด๋‹ค.

 


[ JWT์‚ฌ์šฉ๋ฐฉ๋ฒ• ]

 

1) jsonwebtoken ์˜คํ”ˆ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ JWT๋ฅผ ์ด์šฉํ•œ๋‹ค.

# yarn์„ ์ด์šฉํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
yarn init -y

# jsonwebtoken, express ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
yarn add jsonwebtoken express

* ์ด๋•Œ, yarn์„ ์ด์šฉํ•ด ์ƒ์„ฑ๋œ package.json ํŒŒ์ผ์—์„œ type์„ module๋กœ ๊ผญ! ๋ณ€๊ฒฝํ•ด์•„ํ•œ๋‹ค.

 

2) JSON ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•œ๋‹ค.

jsonwebtoken ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ sign ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด JWT๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

import jwt from 'jsonwebtoken';

const token = jwt.sign({ myPayloadData: 1234 }, 'mysecretkey');
console.log(token); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0

* sign ๋ฉ”์„œ๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ Payload ๋ฐ์ดํ„ฐ๋ฅผ, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋น„๋ฐ€ ํ‚ค๋ฅผ ๋ฐ›์•„ JWT๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

→ ์—ฌ๊ธฐ์„œ, Payload๋Š” ๋ฌธ์ž์—ด๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๊ฐ์ฒด๋„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.

 

3) ๋ณตํ˜ธํ™”ํ•ด๋ณด๊ธฐ
- jsonwebtoken ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ decode ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด JWT๋ฅผ ๋ณตํ˜ธํ™”ํ•œ๋‹ค.

import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValue = jwt.decode(token);

console.log(decodedValue); // { myPayloadData: 1234, iat: 1690873885 }

- jsonwebtoken ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ verify ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด JWT๋ฅผ ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValueByVerify = jwt.verify(token, "mysecretkey");

console.log(decodedValueByVerify); // { myPayloadData: 1234, iat: 1690873885 }

* JWT๊ฐ€ ๋ณ€์กฐ๋˜์ง€ ์•Š์•˜๊ณ , ์˜ฌ๋ฐ”๋ฅธ ๋น„๋ฐ€ ํ‚ค๋กœ ์„œ๋ช…๋˜์—ˆ๋Š”์ง€๋ฅผ ๊ฒ€์ฆ. ๊ฒ€์ฆ์— ์‹คํŒจํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValueByVerify = jwt.verify(token, "secretkey");

console.log(decodedValueByVerify);

// JsonWebTokenError: invalid signature

* ์ž˜๋ชป๋œ ๋น„๋ฐ€ํ‚ค (mysecretkey๋ฅผ secretkey๋กœ ๋ณ€๊ฒฝํ•ด์„œ) ์‹คํ–‰. ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜์–ด, ๊ฒ€์ฆ์— ์‹คํŒจํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[ JWT๋ฅผ ์ ์šฉํ•œ ๋กœ๊ทธ์ธ API ์˜ˆ์‹œ ]

import express from 'express';
import JWT from 'jsonwebtoken';

const app = express();

app.post('/login', (req, res) => {
  // ์‚ฌ์šฉ์ž ์ •๋ณด
  const user = {
    userId: 203,
    email: 'archepro84@gmail.com',
    name: '์ด์ด๋ฆ„',
  };


// *** userJWT๋ฅผ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•œ๋‹ค.  JWT.sign์ด๋ผ๋Š” ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด 
// ์œ„์˜ ์‚ฌ์šฉ์ž์ •๋ณด๋ฅผ payload์—๋„ฃ์–ด์คŒ

  // ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ JWT๋กœ ์ƒ์„ฑ
  const userJWT = JWT.sign(
    user, // user ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ payload์— ํ• ๋‹น
    'secretOrPrivateKey', // JWT์˜ ๋น„๋ฐ€ํ‚ค๋ฅผ secretOrPrivateKey๋ผ๋Š” ๋ฌธ์ž์—ด๋กœ ํ• ๋‹น
    { expiresIn: '1h' }, // JWT์˜ ์ธ์ฆ ๋งŒ๋ฃŒ์‹œ๊ฐ„์„ 1์‹œ๊ฐ„์œผ๋กœ ์„ค์ •
  );

  // userJWT ๋ณ€์ˆ˜๋ฅผ sparta ๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ์ฟ ํ‚ค์— Bearer ํ† ํฐ ํ˜•์‹์œผ๋กœ ํ• ๋‹น
// *** bearer์€ ํ† ํฐ์„ ์ „๋‹ฌํ•˜๋Š” ํ•˜๋‚˜์˜ ํ˜•์‹์ด๋‹ค! 
  res.cookie('sparta', `Bearer ${userJWT}`);
  return res.status(200).end();
});

app.listen(5002, () => {
  console.log(5002, '๋ฒˆํ˜ธ๋กœ ์„œ๋ฒ„๊ฐ€ ์ผœ์กŒ์–ด์š”!');
});