소스 : https://github.dev/WebDevSimplified/JWT-Authentication
https://github.com/braverokmc79/Nodejs-jwt-Authentication-Tutorial

1.설치 :
$ npm init -y $ npm i express jsonwebtoken dotenv $ npm i nodemon --save-dev $ npm install concurrently --save-dev
2. package.json 설정
"scripts": {
"devStart": "nodemon server.js",
"devStartAuth": "nodemon authServer.js",
"dev": "concurrently \"npm run devStartAuth \" \"npm run devStart\" ",
"test": "echo \"Error: no test specified\" && exit 1"
},
실행
$npm run dev
3. vscode "REST Client" 확장 패키지 설치
: REST 클라이언트를 사용하면 HTTP 요청을 보내고 Visual Studio Code에서 직접 응답을 볼 수 패키지

requests.rest 파일 생성
작성
GET http://localhost:3000/posts
상단의 Send Request 클릭
4. 토큰 SECRET 값 생성
ACCESS_TOKEN_SECRET
REFRESH_TOKEN_SECRET
$node
require('crypto').randomBytes(64).toString('hex')
.env
ACCESS_TOKEN_SECRET=c066de3987e36822cfdd2eb42cb0ef83fc265767c88bacb07a3dbe776ec631d1e7a56391a357bd6c7b35886693d683ea4838c860871bd8c43d436bfe247b338f REFRESH_TOKEN_SECRET=9fc953c844c4aefc39e7cc6d21ddb9f2ad04817f4d38f7cb877aac6efffc79c89c08900385bbb152bcb139f3a5b973ca64a2e8c3becda3cb1e4cac5e46e90fe2
5.토큰 생성
1) authServer.js (토큰 만료기간은 60초 설정)
require('dotenv').config()
const express = require('express')
const app = express()
const jwt = require('jsonwebtoken')
app.use(express.json())
//DB 대신에 발급한 갱신토큰값을 저장하는 변수
let refreshTokens = []
~
//로그인시 토큰 생성
app.post('/login', (req, res) => {
// Authenticate User
const username = req.body.username
const user = { name: username }
//전근 토큰을 발행
const accessToken = generateAccessToken(user)
//갱신토큰 발행
const refreshToken = jwt.sign(user, process.env.REFRESH_TOKEN_SECRET)
//DB 대신에 refreshTokens 변수에 발급한 갱신 토큰값을 저장한다.
refreshTokens.push(refreshToken)
console.log("1.로그인 토큰 발급 유저 아이디: ", req.body);
console.log("2.로그인 토큰 발급 accessToken: ", accessToken);
console.log("3.로그인 토큰 발급 refreshToken: ", refreshToken);
//json 으로 반환처리
res.json({ accessToken: accessToken, refreshToken: refreshToken })
})
// 시크릿 토큰 키값(ACCESS_TOKEN_SECRET)을 통해 토큰을 발행한다.(만료기간은 60초)
function generateAccessToken(user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '60s' })
}
app.listen(4000)
2)requests.rest (REST AP 테스트로 requests 파일에서 다음과 같이 코드 작성후 실행 - 토큰값은 만료 안된 토큰값으로 변경)
POST http://localhost:4000/login
Content-Type: application/json
{
"username": "Jim"
}
3) 출력 코드 예
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 304
ETag: W/"130-11XDfo54c5Na3WcJ+4d8Xa54uKU"
Date: Sun, 23 Oct 2022 23:04:35 GMT
Connection: close
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTY2Mjc1LCJleHAiOjE2NjY1NjY0MjV9.y2QSzh4CGeYRQ7Iv2Luj9x9rbNgb9LrXHiAQ6z8MZuE",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTY2Mjc1fQ.ov91WzgOA4MWZXFI26E0idRr2QTEQU16lvIOO9bTies"
}
5.토큰 갱신
1) authServer.js
require('dotenv').config()
const express = require('express')
const app = express()
const jwt = require('jsonwebtoken')
app.use(express.json())
//DB 대신에 발급한 갱신토큰값을 저장하는 변수
let refreshTokens = []
//토큰 갱신
app.post('/token', (req, res) => {
//refreshToken 값을 가져온다.
const refreshToken = req.body.token
//refreshToken 값이 없다면 401 에러 (유효한 인증 자격 증명이 없을때 코드 401)
if (refreshToken == null) return res.sendStatus(401)
// Forbidden으로 서버가 허용하지 않는 코드 403 내보낸다.
if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)
//토큰 확인
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
//유효한 갱신토큰이면 accessToken 새로이 발급처리한다.
const accessToken = generateAccessToken({ name: user.name })
res.json({ accessToken: accessToken })
})
})
app.listen(4000)
2)requests.rest (REST AP 테스트로 requests 파일에서 다음과 같이 코드 작성후 실행 - 토큰값은 만료 안된 토큰값으로 변경)
POST http://localhost:4000/token
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTY4NTA1fQ.85e9EJ2lyIy4GSf5lnIM1t6UDwhSWo6LCom3r3_O8j4"
}
3) 출력 코드 예
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 163
ETag: W/"a3-IxnfY3ZCOcsNbBDTjpUiGUhnorw"
Date: Mon, 24 Oct 2022 00:34:26 GMT
Connection: close
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTcxNjY2LCJleHAiOjE2NjY1NzE3MjZ9.2ovAQ748qJ2-snLZfQ3pFfQ9ILOVqOm1aAv0_zyEzwA"
}
6.로그아웃 토큰 삭제
1) authServer.js
require('dotenv').config()
const express = require('express')
const app = express()
const jwt = require('jsonwebtoken')
app.use(express.json())
//DB 대신에 발급한 갱신토큰값을 저장하는 변수
let refreshTokens = []
//로그아웃 기존의 토큰 삭제
app.delete('/logout', (req, res) => {
refreshTokens = refreshTokens.filter(token => token !== req.body.token)
res.sendStatus(204)
})
app.listen(4000)
2)requests.rest (REST AP 테스트로 requests 파일에서 다음과 같이 코드 작성후 실행 - 토큰값은 만료 안된 토큰값으로 변경)
DELETE http://localhost:4000/logout
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTc0NDM4LCJleHAiOjE2NjY1NzQ0OTh9.gPS7j08qyrRSP7iRDM6-2f3bpU7pA7TnA1fjSzbXkpw"
}
3) 출력 코드 예
HTTP/1.1 204 No Content X-Powered-By: Express ETag: W/"a-bAsFyilMr4Ra1hIU5PyoyFRunpI" Date: Mon, 24 Oct 2022 01:20:55 GMT Connection: close
7.server.js 3000 포트 서버에서 토큰 테스트
server.js
require('dotenv').config()
const express = require('express')
const app = express()
const jwt = require('jsonwebtoken')
app.use(express.json())
const posts = [
{
username: 'Kyle',
title: 'Post 1'
},
{
username: 'Jim',
title: 'Post 2'
}
]
app.get('/posts', authenticateToken, (req, res) => {
res.json(posts.filter(post => post.username === req.user.name))
})
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token == null) return res.sendStatus(401)
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
console.log(err)
if (err) return res.sendStatus(403)
req.user = user
next()
})
}
app.listen(3000)
2)requests.rest (REST AP 테스트로 requests 파일에서 다음과 같이 코드 작성후 실행 - 토큰값은 만료 안된 토큰값으로 변경)
GET http://localhost:3000/posts Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmltIiwiaWF0IjoxNjY2NTQ2MDI4LCJleHAiOjE2NjY1NDYxNzh9.QVn9H42Fv-YtfMqo7w62vy4iTC5W2fMksOmUhA_gHNc
3)출력 코드 예
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 37
ETag: W/"25-+oLv3OVWqMVRmz334tj9PEFk5V4"
Date: Mon, 24 Oct 2022 01:40:48 GMT
Connection: close
[
{
"username": "Jim",
"title": "Post 2"
}
]
토큰 만료 시
HTTP/1.1 403 Forbidden X-Powered-By: Express Content-Type: text/plain; charset=utf-8 Content-Length: 9 ETag: W/"9-PatfYBLj4Um1qTm5zrukoLhNyPU" Date: Mon, 24 Oct 2022 01:35:02 GMT Connection: close Forbidden
8. authServer.js 전체 코드
require('dotenv').config()
const express = require('express')
const app = express()
const jwt = require('jsonwebtoken')
app.use(express.json())
//DB 대신에 발급한 갱신토큰값을 저장하는 변수
let refreshTokens = []
//토큰 갱신
app.post('/token', (req, res) => {
//refreshToken 값을 가져온다.
const refreshToken = req.body.token
//refreshToken 값이 없다면 401 에러 (유효한 인증 자격 증명이 없을때 코드 401)
if (refreshToken == null) return res.sendStatus(401)
// Forbidden으로 서버가 허용하지 않는 코드 403 내보낸다.
if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)
//토큰 확인
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
//유효한 갱신토큰이면 accessToken 새로이 발급처리한다.
const accessToken = generateAccessToken({ name: user.name })
res.json({ accessToken: accessToken })
})
})
//로그아웃 기존의 토큰 삭제
app.delete('/logout', (req, res) => {
refreshTokens = refreshTokens.filter(token => token !== req.body.token)
res.sendStatus(204)
})
//로그인시 토큰 생성
app.post('/login', (req, res) => {
// Authenticate User
const username = req.body.username
const user = { name: username }
//전근 토큰을 발행
const accessToken = generateAccessToken(user)
//갱신토큰 발행
const refreshToken = jwt.sign(user, process.env.REFRESH_TOKEN_SECRET)
//DB 대신에 refreshTokens 변수에 발급한 갱신 토큰값을 저장한다.
refreshTokens.push(refreshToken)
console.log("1.로그인 토큰 발급 유저 아이디: ", req.body);
console.log("2.로그인 토큰 발급 accessToken: ", accessToken);
console.log("3.로그인 토큰 발급 refreshToken: ", refreshToken);
//json 으로 반환처리
res.json({ accessToken: accessToken, refreshToken: refreshToken })
})
// 시크릿 토큰 키값(ACCESS_TOKEN_SECRET)을 통해 토큰을 발행한다.(만료기간은 60초)
function generateAccessToken(user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '60s' })
}
app.listen(4000)













댓글 ( 4)
댓글 남기기