Nodejs

 

 

 

 

소스 : 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)

 

 

 

 

 

about author

PHRASE

Level 60  라이트

동물의 경우, 성욕은 일상적인 힘이다. 그러나 인간의 경우에는 에로티시즘이 되고 위험에 도전하며 동요를 초래하는 힘으로 변하다. 인간의 경우에만 예측할 수 없는 힘이 되는 것은 그것이 무한한 공상력(空想力)에 의해서 자라기 때문이다. -프란체스코 알베로니

댓글 ( 4)

댓글 남기기

작성

Nodejs 목록    more