이번 포스팅에서는 JWT 방식으로 로그인 인증을 구현해 보고자 한다. 자세한 개념과 구현방식은 이전 글에서 정리했으므로 이번에는 코드 위주로 빠르게 훑어 보도록 하자.
(이전 글 참고)
2022.03.03 - [Node.js/express] - Node.js - express (12) Buffer , Hash , JWT
위에 나와있는 영상은 앞으로 우리가 작성할 코드의 결과물이다. 간단히 설명하자면 메인페이지에서 로그인 페이지로 이동한 후 로그인에 성공하면 JWT가 쿠키에 담겨서 브라우저에 저장되고 로그인 인증 결과 메인 페이지가 다르게 랜더된다. 그리고 로그아웃 버튼을 누르면 쿠키가 삭제되면서 로그인이 풀리고 메인페이지가 로그인 이전 화면으로 랜더된다.
이제 코드를 살펴보자. 우선 메인페이지(index.html)와 로그인페이지(login.html)의 html은 다음과 같다. 참고로 템플릿 엔진 nunjucks를 사용하여 html을 작성하였다.
다음은 메인 실행 파일인 server.js 파일의 코드이다.
// server.js
const express = require('express')
const nunjucks = require('nunjucks')
const cookieParser = require('cookie-parser')
const {user} = require('./models/user.js')
const {createToken} = require('./utils/jwt.js')
const {auth} = require('./middlewares/auth.js')
const app = express()
app.set('view engine', 'html')
nunjucks.configure('./views', {
express: app,
watch: true
})
app.use(express.urlencoded({extended: true}))
app.use(cookieParser())
app.use(auth)
app.get('/', (req, res)=>{
const {user} = req
res.render('index.html', {
user
})
})
app.get('/login', (req, res)=>{
res.render('login.html')
})
app.post('/login', (req, res)=>{
const {userid, userpw} = req.body
const [ item ] = user.filter(v => v.userid == userid && v.userpw == userpw)
try {
if (item == undefined) throw new Error('일치하는 아이디가 존재하지 않음')
const payload = {
userid: item.userid,
username: item.username,
level: 1
}
// 만들어 놓은 함수를 이용해 JWT 생성
const token = createToken(payload)
// 생성한 토큰을 쿠키로 만들어서 브라우저에게 전달
res.cookie('AccessToken', token, {
path: '/',
HttpOnly: true
})
res.redirect('/')
} catch(err) {
console.log(err)
res.send('로그인 실패')
}
})
app.get('/logout', (req, res)=>{
// 쿠키 삭제
res.clearCookie('AccessToken', {path: '/'})
res.redirect('/')
})
app.listen(3000, ()=>{
console.log('server onload')
})
user.js 파일에 있는 사용자 정보와 입력받은 정보가 일치할 경우 createToken 함수에 의해 JWT가 생성된다. res.cookie( )를 통해 브라우저에 쿠키를 저장하고 메인페이지로 redirect 시킨다. app.use(auth)에 의해 모든 라우터에서는 인증 절차를 거치게 되고 nunjucks 템플릿 엔진을 사용해서 req 객체 안에 user 속성값의 존재 유무에 따라 메인페이지가 다르게 랜더되도록 처리하였다. 여기서 JWT를 생성할 때 사용한 createToken 함수와 인증 과정을 구현한 auth 함수는 다음과 같다.
// JWT 생성 함수
// jwt.js
const crypto = require('crypto')
const salt = 'ingoo'
function createToken(state) {
// JWT에 필요한 필수요소 - header, payload, signature
const header = {
typ: 'JWT',
alg: 'HS256'
}
const payload = {
...state
}
// encoding한 헤더와 페이로드를 이용해서 signature 생성
const encodingHeader = encoding(header)
const encodingPayload = encoding(payload)
const signature = createSignature(encodingHeader, encodingPayload)
return `${encodingHeader}.${encodingPayload}.${signature}`
}
// 인코딩 함수
function encoding(value) {
return Buffer.from(JSON.stringify(value)).toString('base64').replace(/[=]/g, '')
}
// signature 생성 함수
function createSignature(header, payload) {
const encoding = `${header}.${payload}`
const signature = crypto.createHmac('sha256', salt)
.update(encoding)
.digest('base64')
.replace(/[=]/g, '')
return signature
}
module.exports = {
createToken,
createSignature
}
// auth 함수 (인증)
// auth.js
const {createSignature} = require('../utils/jwt.js')
const auth = (req, res, next) => {
try {
const {AccessToken} = req.cookies
const header = AccessToken.split('.')[0]
const payload = AccessToken.split('.')[1]
const signature = AccessToken.split('.')[2]
const sign = createSignature(header, payload)
if (sign !== signature) throw new Error('poisoned cookie')
const user = JSON.parse(Buffer.from(payload, 'base64').toString('utf-8'))
req.user = {
...user
}
next()
} catch {
next()
}
}
module.exports = {
auth
}
auth 함수를 살펴보면 브라우저의 쿠키에 저장된 JWT를 이용해 인증을 진행하고 있는 것을 확인할 수 있다. JWT의 header와 payload 부분을 가져와서 다시 signature를 생성하고 sign이라는 변수에 담는다. 그리고 쿠키에 저장되어 있던 signature와 새롭게 생성한 signature(sign 변수에 담긴 값)를 비교하여 같은 값이면 인증을 완료한다. 만약 다른 값이라면 이는 쿠키가 조작된 것이므로 에러를 발생시켰다. 그리고 인증이 완료되었을시 payload에 있는 사용자 정보를 이용하기 위해 JSON.parse( )를 사용해서 payload를 디코딩하였다. 디코딩된 내용은 req 객체 안의 user 속성값으로 담아서 이용하였다.
이전 글에 작성된 개념과 원리를 바탕으로 작성된 코드이기에 비교적 간략하게 설명을 진행하였다. 혹시 이해하기 난해하다면 이전 글을 참고해보길 바란다.
2022.03.03 - [Node.js/express] - Node.js - express (12) Buffer , Hash , JWT
'Node > Express' 카테고리의 다른 글
Node.js - express (15) Ajax - fetch , axios (0) | 2022.03.07 |
---|---|
Node.js - express (14) Ajax - XMLHttpRequest( ) (0) | 2022.03.06 |
Node.js - express (12) Buffer , Hash , JWT (0) | 2022.03.03 |
Node.js - express (11) express.json() (0) | 2022.02.15 |
Node.js - express (10) express-session 사용하기 (2) | 2022.02.12 |