소스 : 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)
댓글 남기기