Node.js jwt 예제 - node.js jwt yeje

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

이번 포스팅에서는 JWT를 이용해 사용자의 로그인과정을 거치는 간단한 예제를 만들어 보고자 합니다.

 

JWT는 아래 방법으로 설치할 수 있습니다.

npm i jwt

JWP설 설치하고 나면 .env 파일에 임의의 키값을 저장해 둡니다. 이 키값은 외부에 노출되어서는 안 됩니다.

JWT_SECRET=키값

다음으로 JWT토큰을 확인하여 만료 여부와 유효성을 파악한 다음 디코딩한 정보를 담아두는 미들웨어를 추가합니다. 에제에서는 이 미들웨어를 login디렉터리 안에 userjwt.js 파일명으로 저장하였습니다.

const jwt = require('jsonwebtoken'); require('dotenv').config(); exports.verifyToken = (req, res, next) => { try { req.decoded = jwt.verify(req.cookies.user, process.env.JWT_SECRET); return next(); } catch (error) { if (error.name === 'TokenExpiredError') { return res.status(419).json({ code: 419, message: '토큰만료' }); } return res.status(401).json({ code: 401, message: '유효하지 않은 토큰' }); } };

예제는 보시면 jwt의 verify() 함수를 통해 토큰과 위에서 설정한 키값을 통해 해당 토큰의 유효성을 확인하고 있습니다. 아무런 문제가 없다면 decoded 안에 토큰을 복호화(디코딩)한 결괏값을 저장할 것입니다.

 

login 디렉터리 안에 다음과 같은 HTML 파일을 생성합니다. 사실 이번 예제에서는 사용자의 비밀번호 여부는 확인하지 않기 때문에 비밀번호를 입력하는 부분은 필요하지 않습니다.

<!DOCTYPE html> <html> <head> <title></title> </head> <body> <form action="/login/token" method="post"> 아이디 : <input type="test" id="id" name="id" /><br /> 비밀번호 : <input type="password" id="pw" name="pw" /><br /> <button type="submit">확인</button> </form> </body> </html>

login디렉터리에 index.js 파일을 다음과 같은 내용으로 추가합니다.

const express = require('express'); const jwt = require('jsonwebtoken'); const cookieParser = require('cookie-parser'); const { verifyToken } = require('./userjwt'); const router = express.Router(); router.use(express.urlencoded({ extended: true })); router.use(cookieParser()); router.get('/', (req, res) => { res.sendFile(`${__dirname}/login.html`); }); router.post('/token', (req, res) => { const token = jwt.sign({ id: req.body.id, name: 'abcdefg' }, process.env.JWT_SECRET, { expiresIn: '5m' }); res.cookie('user', token, { httpOnly: true }); res.send('발급됨'); }); router.get('/user', verifyToken, (req, res) => { res.send(req.decoded); }); router.get('/test', (req, res) => { res.send(req.cookies.user); }); module.exports = router;

토큰은 쿠키에 저장하므로 cookie를 사용할 수 있도록 해야 하며 login으로 라우트를 분리한 상태입니다.

 

사용자가 /user를 요청하면 위에서 만들어둔 login.html 파일을 응답합니다. login.html에서 로그인을 시도하면 해당 정보를 /login/token으로 보내고 입력한 id값을 받아 JWT토큰을 생성한 뒤 쿠키에 담다 둠으로 서 토큰 발급을 완료합니다.

 

토큰을 발급할 때는 jwt의 sign() 함수를 사용하며 sign에 토큰으로 들어갈 객체를 설정하고 expiresIn속성에 토큰의 유효시간을 설정합니다. 예제에서는 5m으로 했는데 5분을 의미하며 h를 사용해 시간 단위로 설정할 수도 있습니다.

 

아후 /login/user를 요청하면 verifyToken 미들웨어를 거쳐 쿠키로 전달되어온 토큰 값이 유효한지 여부를 확인합니다. 아무런 문제가 없으면 decoded안에 결괏값을 답게 되고 사용자에게 req.decoded값을 읽어 표시합니다. 사용자 인증이 필요한 모든 라우트에 이와 같이 verifyToken을 붙여두고 인증절차를 거치면 됩니다.

이 강의에서는 Node.js에 대한 배경지식이 있어야합니다. Node.js 를 잘 모르시는분들은 Node.js 기초 강의 를 먼저 읽어주세요. 추가적으로, 토큰 기반 시스템 (포스트 i)과 JWT(포스트 ii)에 대한 이해가 필요하니, 지난 포스트들을 읽지 않으신분들은 강의를 시작하기전에 한번 참조해주세요.

추가적으로, 이 강의에서는 ES6 문법을 사용합니다.

* 이 강좌를 진행 하면서 이해가 가질 않거나 궁금한것이 있으면 언제든지 덧글로 달아주세요.

소개

회원인증 시스템은 모든 어플리케이션에서 정말 중요한 부분입니다. 기존의 어플리케이션들은 세션 기반 회원인증 시스템을 많이 사용해왔습니다. 지금도, 많은 곳에서 사용되고 있죠. 하지만 요즘은 토큰을 기반으로 회원인증 시스템을 구축하는 서비스 회사들이 늘어가고있습니다.  Facebook, LinkedIn, Instagram, GitHub, Google 등 수많은 공룡급 회사에서 사용을 하고있지요. 지금은 이런 인증 시스템이 가히 ‘대세‘ 라고 할 수 있겠습니다. (주관적인 표현입니다.)

이를 사용함으로서, 서버측의 확장성이 높아지고, 보안시스템을 강화 할 수 있습니다. 하지만 무조건 좋은것만은 아니겠죠. 토큰을 사용함으로서 보안시스템을 강화 할 수도 있겠지만, 오히려 잘못사용하면 매우 취약해지기때문에 잘 알고 사용하는것이 좋습니다.

토큰기반인증 시스템은 API 모델을 가진 어플리케이션에 매우 적합합니다. 요즘은 Angular2, React, Vue 등을 사용해서 많은 서비스들이 만들어져있고 그런 자바스크립트 라이브러리 혹은 프레임워크를 사용하는 어플리케이션들은 REST API 던 GraphQL API 던.. 정말 많이 의존하고 있죠. 모바일앱도 마찬가지구요.

세션기반인증 시스템도 충분히 제구실을 하지만, 여러분이 ‘모던 웹/앱 개발자’라면, 토큰기반인증 시스템은 한번쯤 배워볼 가치가있습니다. 사용해보고, 여러분의 서비스에 적합하다 싶으면 도입을 하면되죠.

자, 그럼 시작해봅시다!

이 프로젝트에 사용된 코드는 GitHub 에서 에서 열람 하실 수 있습니다.

준비물

이 강의를 진행하기 위해 필요한 주요 준비물은 다음과 같습니다.

  1. Node.js LTS 버전(현재 기준 6.91) 과 npm
  2. MongoDB 서버 (강의에서는 편의상 mLab 에서 호스팅을 받아 사용합니다. 본인이 원한다면 몽고디비 서버를 직접 설치하여 사용해도 됩니다)
  3. 코드 에디터 (자신이 가장 좋아하는 에디터를 사용하세요. 강의에서는 VS Code 를 사용하도록 하겠습니다)
  4. POSTMAN – API 테스팅 크롬 확장 프로그램

 

#1 프로젝트 생성 및 설정하기

프로젝트 설정

먼저 npm install --save express body-parser jsonwebtoken mongoose morgan8 이라는 디렉토리를 만들은후, 터미널로 해당 디렉토리를 열어 npm 으로 프로젝트를 생성하세요.

npm -y

그러면 디렉토리내에서 프로젝트를 기본설정으로 설정하게되면서 npm install --save express body-parser jsonwebtoken mongoose morgan9 파일이 생성됩니다. 한번 열어보세요.

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

잘 됐나요? 아직은 npm 을 사용하여 설치한 모듈들이 없기때문에, dependency 가 존재하지 않습니다.

의존(dependency) 모듈 설치

자, 이제 저희 프로젝트에서 필요한 의존 모듈을 설치하겠습니다.

npm install --save express body-parser jsonwebtoken mongoose morgan
  • express: 저희 프로젝트에서 사용 할 웹서버 프레임워크입니다.
  • body-parser: 클라이언트측에서 요청을 받을때, url-encoded 쿼리 및 json 형태의 바디를 파싱하는데 도움을 주는 모듈입니다.
  • jsonwebtoken: 이 예제프로젝트에서 사용되는 핵심 모듈입니다. JSON Web Token 을 손쉽게 생성하고, 또 검증도 해줍니다.
  • mongoose: 서버에서 MongoDB 를 사용하기 위하여 설치합니다.
  • morgan: Express 서버에서 발생하는 이벤트들을 기록해주는 미들웨어입니다

설치가 끝났으면, 다시 npm install --save express body-parser jsonwebtoken mongoose morgan9 을 확인해보세요. { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 1 은 생략되었다는 의미 입니다.

{ (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } }

디렉토리 구조

프로젝트에서 사용 할 디렉토리 구조는 다음과 같습니다. 각 파일들은 진행을 하면서 하나하나 생성 할 것이며 설명 또한 진행하면서 하도록 하겠습니다.

문법 검사를 위하여 { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 2 를 사용하고있습니다. 위 프로젝트의 { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 2 설정 파일의 내용은 여기서 확인할수있습니다. { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 2 를 설정하는건 필수는 아닙니다. 사용을 원하시는 분들은 { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 5 파일을 생성하시고 npm 으로 { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 6 를 설치하세요

npm install --save-dev eslint

 

#2 User 스키마 작성하기

mongoose 에서 User 정보를 MongoDB 에 넣기 위해선, 스키마를 먼저 만들어야합니다.
{ (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 7 에 다음 코드를 작성해주세요

models/user.js

const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)

앞으로 User 모델을 사용하여 처리를 하기 위한 작업들을 멤버 / 스태틱 메소드로 미리 준비했습니다.

  • { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 8 메소드는 새 유저를 생성합니다. 원래는 이 메소드처럼 비밀번호를 그대로 문자열 형태로 저장하면 보안적으로 매우 나쁩니다. 일단 지금은 배우는 과정이니 간단하게 문자열로 저장을 하지만, 포스트의 후반부에서는 비밀번호를 해쉬하여 저장하도록 하겠습니다
  • { (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 9 메소드는 username 값을 사용하여 유저를 찾습니다
  • npm install --save-dev eslint 0 메소드는 비밀번호가 정확한지 확인을 합니다. 지금은 그냥 npm install --save-dev eslint 1 를 사용해서 비교 후 결과를 반환하지만 포스트 후반부에서는 해쉬를 확인하여 결과를 반환하겠습니다
  • npm install --save-dev eslint 2 메소드는 유저를 관리자 계정으로 설정해줍니다. 저희 예제 프로젝트에서는, 가장 처음으로 가입한 사람과, 관리자가 나중에 API 를 사용하여 지정한사람이 관리자 권한을 부여 받습니다.

 

#3 MongoDB 준비 및 설정 파일 (config.js) 만들기

npm install --save-dev eslint 3 파일은 우리 예제 프로젝트에서 사용할 MongoDB 서버의 정보와, JWT 토큰을 만들 때 사용 될 npm install --save-dev eslint 4 키의 정보를 지니고있습니다. 이렇게 보안에 관련된 정보는 따로 파일에 분리하여 관리를 하는게 좋습니다. 이렇게 하면, 예를들어, github 에 오픈 소스를 할 때, .gitignore 에 추가해서 해당 파일은 싱크가 되지 않도록 설정 할 수 있겠죠? (이 프로젝트의 github 저장소에서도 config.js 파일은 커밋이 되어있지 않습니다. 예제 정보가 적혀있는 config.example.js 파일의 이름을 config.js 로 수정하고 사용해야합니다.

설정 파일을 작성하기 전에, MongoDB 서버를 준비해주어야 합니다. 개인 서버에서 MongoDB 를 돌려도 되고, 아니면 mLab 이라는 MongoDB 호스팅 서비스를 사용해도 됩니다. 이 포스트에서는 mLab 을 사용하도록 하겠습니다.

mlab 가입 및 데이터베이스 생성

자, 이제 데이터베이스를 설정하는 과정이 끝났습니다. mLab 에서 데이터베이스 페이지의 상단을 보시면 다음과 같은 텍스트가 있습니다.

이 값을 복사하세요.

config.js 파일 작성

이제, npm install --save-dev eslint 3 파일을 생성하여 다음 코드를 작성하세요.

config.js

module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }

위에서 복사했던 MongoDB URI 를 코드에 붙여넣고, npm install --save-dev eslint 6 가 있던 부분에 여러분이 아까 만들었던 계정 정보를 입력하세요.

여기서 npm install --save-dev eslint 4 은 나중에 JWT 토큰을 검증하는 서명부분을 만들 때, 해싱 알고리즘에서 사용 될 비밀 키 입니다.

 

#4 서버 코드 작성하기

이제 서버의 메인 부분인 npm install --save-dev eslint 8 의 코드를 작성해봅시다.
주요코드를 먼저 작성하고, 추후 라우터들을 만든 다음에 또 수정 하겠습니다

app.js

/* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })

코드 작성이 완료되었다면, 터미널에서 npm install --save-dev eslint 9 를 입력하여 서버를 실행해보세요.

> node app.js Express is running on port 3000 connected to mongodb server

잘 되었나요? 그렇다면 Postman을 열어서 //localhost:3000/ 에 GET 요청을 해보세요.

Hello JWT 라고 응답을 하네요! 게속해서 진행해봅시다..

앞으로 서버를 수정하고 테스팅을 할 때 마다 서버를 재시작해주어야 합니다. 재시작 하는게 귀찮다면 const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)0 도구를 사용해서 서버를 실행하면 서버가 수정 될 때마다 자동으로 재시작됩니다.

npm install -g nodemon nodemon server.js

 

#5 회원가입 구현

npm install --save-dev eslint 8 파일에서 모든 코드를 입력해버리면 파일도 너무 커지고 보기 힘드니까, 라우터를 작성해봅시다. 우리는 라우터의 기본 틀을 먼저 만들고 나서, 기능구현은 그 다음에 하도록 하겠습니다.

회원가입 API 준비하기

const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)2 라우터부터 작성을 해볼까요?  회원가입의 라우트는 const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)3 로 설정하도록 하겠습니다.

const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)4 파일을 생성하여 다음 코드를 작성하세요.

routes/api/auth/controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 0

회원가입 API 의 기본 틀을 만들어놓았습니다.

그 다음엔, const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)5 파일에서 방금 만든 컨트롤러 파일을 불러온 후, 라우터를 정의해봅시다.

routes/api/auth/index.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 1

우리는 const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)2 라는 라우터를 정의했습니다.

이제 상위 폴더로 올라가서 const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)7 라는 라우터를 정의 한 뒤, 해당 라우터에서 /const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)2 로 요청이 들어오면 위 라우터로 연결시켜줍시다.

routes/api/index.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 2

코드가 거의 비슷하지요?

마지막으로, npm install --save-dev eslint 8 파일을 다시 열어서 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }0 경로로 요청이 들어오면 위 라우터로 연결시켜줍시다.

app.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 3

지금까지 한 작업을 정리하자면, const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)2 → const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)7 → module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }3 순으로 한단계 한단계 거슬러 올라간 것 입니다. 자 이제 코드를 저장 후, Postman 으로 //localhost:3000/api/auth/register 경로에 POST 요청을 해보세요

제대로 작동 하는군요.

 

회원가입 API 기능 구현하기

회원가입 기능을 구현하기전에 우리 프로젝트에서 회원가입부분에서 사용할 알고리즘을 알아봅시다

회원가입 요청이 들어오면, 아이디가 중복되는지 확인을 하고, 새 회원을 등록합니다. 그리고 만약에 해당 유저가 첫번째 유저라면, 그 유저를 관리자 권한을 부여합니다.

이 작업에서 우리는 MongoDB에 3번의 쿼리를 해야합니다. 데이터베이스에 쿼리를 하는것은, 비동기 작업이죠. Node.js 에서 비동기작업은 보통 콜백으로 처리를하지요. 하지만, 이렇게 비동기작업이 많아지면 콜백안에 콜백안에 콜백이 생기게됩니다.

콜백이 많아지면… 콜백지옥이라고도 부르죠.

업데이트된 자바스크립트의 문법 ES6 에서는 Promise 라는 기능을 지원하여 조금더 코드를 보기 좋게 작성 할 수 있게 해줍니다.  만약에 Promise 에 대해서 잘 모르신다면 Webframeworks.kr 의 ES6 Promises(1) – the API 포스트를 참고하세요.

 

자, 이제 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }4 파일을 열어서 상단에 User 모델을 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }5 해주고, 함수에 기능을 구현해봅시다.

/api/auth/auth.controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 4

만약에 Promise 를 사용하지 않았더라면, 조금 더 nested 되어있고 복잡한 구조였겠죠.

코드를 조금 더 깔끔하게 작성하기 위하여 각 작업들을 따로 함수로 만든다음에 Promise chain 을 만들었습니다.

위 구조도, 보기에 꽤 괜찮긴하지만, ES6 보다 더 최신문법인 ES7 에서는 비동기작업을 더 깔끔하게 할 수 있는 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }6 & module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }7 이라는 문법을 지원합니다. 하지만 그 문법을 사용하려면 babel 설정을 해야하기에.. 이 강의에선 Promise 만 사용을 했습니다. 나중에 시간이 있으시면 이 문법도 알아보시길 바랍니다. 정말 편하거든요. (자세한 내용은 여기서)

코드를 저장하고, Postman 으로 회원가입 요청을 넣어보세요.

오호.. 가입에 성공했다고합니다. 관리자로도 지정이 되었습니다. 한번 똑같은 요청을 다시한번 넣어보세요.

유저네임이 중복된다고 뜨네요. 이번엔 username 을 다른 값으로 설정해서 다시 요청을 해보세요

이번엔 관리자 지정이 되지 않았습니다. 저희가 작성한 코드가 제대로 작동을 하는군요.

 

#6 로그인 구현

로그인 API 준비하기

자, 이제 드디어 로그인을 하고 JWT 토큰을 발급받는 방법을 다뤄보도록 하겠습니다.

우선, 회원가입 API 를 만들때 했었던것처럼 로그인 API 도 라우팅을 위한 코드를 미리 작성하겠습니다.

로그인의 라우트는 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }8 으로 설정하겠습니다.

routes/api/auth/auth.controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 5

routes/api/auth/index.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 6

 

로그인 API 기능 구현하기

자, 이제 login 함수의 기능을 구현해줄 차례입니다. jwt 토큰을 발급하려면, 강좌의 시작부분에서 npm 을 통해 설치한 module.exports = { 'secret': 'SeCrEtKeYfOrHaShInG', 'mongodbUri': 'mongodb://velopert:password@ds127428.mlab.com:27428/jwt-tutorial' }9 모듈을 사용해야합니다. 우선, 컨트롤러 파일의 상단에 해당 모듈을 불러와주세요

routes/api/auth/auth.controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 7

jsonwebtoken 으로 JWT 발급하기

사용법: /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })0

만약에 /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })1 이 전달되면 비동기적으로 작동하며, 콜백함수의 파라미터는 /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })2 입니다. 전달되지 않을시엔 동기적으로 작동하며, JWT 를 문자열 형태로 리턴합니다.

/* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })3 는  객체, buffer, 혹은 문자열형태로 전달 될 수있습니다.

npm install --save-dev eslint 4 은 서명을 만들 때 사용되는 알고리즘에서 사용되는 문자열 혹은 buffer 형태의 값 입니다.

/* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })5:

  • /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })6: 기본값은 /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })7 으로 지정됩니다.
  • /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })8: JWT 의 등록된 클레임중 /* ======================= LOAD THE DEPENDENCIES ==========================*/ const express = require('express') const bodyParser = require('body-parser') const morgan = require('morgan') const mongoose = require('mongoose') /* ======================= LOAD THE CONFIG ==========================*/ const config = require('./config') const port = process.env.PORT || 3000 /* ======================= EXPRESS CONFIGURATION ==========================*/ const app = express() // parse JSON and url-encoded query app.use(bodyParser.urlencoded({extended: false})) app.use(bodyParser.json()) // print the request log on console app.use(morgan('dev')) // set the secret key variable for jwt app.set('jwt-secret', config.secret) // index page, just for testing app.get('/', (req, res) => { res.send('Hello JWT') }) // open the server app.listen(port, () => { console.log(`Express is running on port ${port}`) }) /* ======================= CONNECT TO MONGODB SERVER ==========================*/ mongoose.connect(config.mongodbUri) const db = mongoose.connection db.on('error', console.error) db.once('open', ()=>{ console.log('connected to mongodb server') })9 값을 x 초후 혹은 rauchg/ms 형태의 기간 후로 설정합니다.
    (예제: (60, “2 days”, “10h”, “7d”)
  • > node app.js Express is running on port 3000 connected to mongodb server0: > node app.js Express is running on port 3000 connected to mongodb server1 의 등록된 클레임중 > node app.js Express is running on port 3000 connected to mongodb server2 값을 x 초후 혹은 rauchg/ms 형태의 기간 후로 설정합니다.
    (예제: (60, “2 days”, “10h”, “7d”)
  • > node app.js Express is running on port 3000 connected to mongodb server3
  • > node app.js Express is running on port 3000 connected to mongodb server4
  • > node app.js Express is running on port 3000 connected to mongodb server5
  • > node app.js Express is running on port 3000 connected to mongodb server6
  • > node app.js Express is running on port 3000 connected to mongodb server7
  • > node app.js Express is running on port 3000 connected to mongodb server8

더 자세히보기..

이제 > node app.js Express is running on port 3000 connected to mongodb server9 함수 내에서 유저 정보를 확인하고, jwt 토큰을 발급해줍시다

routes/api/auth/auth.controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 8

여기에서도 마찬가지로 각 작업을 함수로 만든다음에, Promise chain을 만들어주었습니다. 함수의 내부에 npm install -g nodemon nodemon server.js0 함수는 유저의 정보를 확인하고 토큰을 발급해주는데요, 여기서 토큰을 비동기적을 만들기에, 내부에서 새로운 Promise 를 만들어 리턴을 하도록 설정하였습니다. 그러면 Promise 에서 이 함수가 실행되면, 로그인이 성공 했을 때 다음. npm install -g nodemon nodemon server.js1 에서는 토큰값을 전달받겠지요.

코드를 저장하고, Postman 을 열어 다음과 같이 로그인 요청을 해봅시다.

토큰이 정상적으로 생성이 되었네요! 한번 이 토큰을 //jwt.io/ 에 붙여넣어볼까요?

잘 만들어졌군요. (서명 부분에서 secret 부분에 우리가 config.js 파일에서 입력한 값을 넣어주어야 합니다)

 

#7 JWT 검증 구현

유저가 JWT 값을 헤더에 x-access-token 으로 설정하거나, url parameter 로 서버로 전달하면, 서버측에서 그 토큰을 가지고 검증 한 후 현재 계정의 상태를 보여주는 기능을 구현해보겠습니다.

계정정보 체킹 라우트는 npm install -g nodemon nodemon server.js2 로 설정하겠습니다.

routes/api/auth/auth.controller.js

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 9

routes/api/auth/index.js

npm install --save express body-parser jsonwebtoken mongoose morgan0

코드를 저장하고 테스팅을 해봅시다. 첫 시도에서는 아까 발급받은 토큰을 npm install -g nodemon nodemon server.js3 헤더값으로 지정하고 다음과 같이 요청을 날려보세요.

잘 되는군요. 두번째 시도는 기존 헤더 값을 지우고, 다음과 같이 url-encoded 쿼리 값을 설정하여 전달을 해보세요.


이 또한 잘 됩니다. 이번엔 토큰 값을 막무가내로 수정하고 요청을 해봅시다.

실패도 제대로 반환이 되네요.

 

#8 JWT 검증 미들웨어 구현

어느정도 감을 잡았죠? 그런데, 토큰을 필요로하는 모든 요청에 토큰검증 코드를 넣기엔 너무나 자주 반복되잖아요? 이 작업을 더 간단하게 하는 방법이 있습니다. 바로 미들웨어죠.

라우터에서 주어진 요청을 설정하기전에, JWT 검증 미들웨어를 통하여 JWT 검증 작업을 하고 나서, 주어진 작업을 하게 하도록 구현을 해봅시다.

npm install -g nodemon nodemon server.js4 파일을 열어 다음 코드를 작성해주세요.

middlewares/auth.js

npm install --save express body-parser jsonwebtoken mongoose morgan1

사전에 작성했었던 npm install -g nodemon nodemon server.js0 함수와 비슷하죠? 이제 이 미들웨어를 npm install -g nodemon nodemon server.js2 라우트에 적용해보겠습니다.

/routes/api/auth/index.js

npm install --save express body-parser jsonwebtoken mongoose morgan2

그러면, npm install -g nodemon nodemon server.js7 안에 있는 npm install -g nodemon nodemon server.js0 함수를 이렇게 간단하게 수정하면됩니다. 만약에 토큰 검증에 실패 될 땐, 미들웨어에서 npm install -g nodemon nodemon server.js9 함수가 실행되지 않기때문에, 이 npm install -g nodemon nodemon server.js0 함수 내부에서는 토큰이 검증실패했을때를 고려하지 않아도 된답니다.

/routes/api/auth/auth.controller.js

npm install --save express body-parser jsonwebtoken mongoose morgan3

여기까지 잘 따라오셨나요? 축하합니다! 이 강의에서 배울 핵심적인 내용을 다 배우셨습니다! 이 포스트 하단부로는 유저 인증 시스템의 추가적인 기능들을 구현하고, { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 01 로 저장하던 비밀번호를 해싱하여 저장하도록 수정합니다.

Node.js 실습을 추가적으로 하고 싶다면 섹션 #9 와 섹션 #10 도 진행을 하시고, 그렇지 않다면 최하단의 마치면서.. 섹션으로 스크롤하세요.

 

 

#9 유저 인증 시스템 추가기능 구현

자, 이제 유저 인증 시스템에서 필요한 두가지의 추가 기능들을 구현해보도록 하겠습니다.

  • 관리자 계정으로 모든 유저 리스팅
  • 관리자 계정으로 특정 유저 관리자권한 부여

user 라우터 컨트롤러 코드 작성

이 라우터에서는 jwt 와 발급 / 검증 작업을 하지 않기 때문에 (미들웨어가 해줄것이기 때문이죠) 작성 할 코드는 꽤나 간단합니다.

routes/api/user/user.controller.js

npm install --save express body-parser jsonwebtoken mongoose morgan4

 

user 라우터 코드 작성

routes/api/user/index.js

npm install --save express body-parser jsonwebtoken mongoose morgan5

{ "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 02 라우터의 경우,라우터 내부의 모든 API 들이 JWT 토큰 검증이 필요하므로, 여기서 { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 03 적용하지 않고, { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 04 파일에서 적용합니다.

 

api 라우터 수정

const mongoose = require('mongoose') const Schema = mongoose.Schema const User = new Schema({ username: String, password: String, admin: { type: Boolean, default: false } }) // create new User document User.statics.create = function(username, password) { const user = new this({ username, password }) // return the Promise return user.save() } // find one user by using username User.statics.findOneByUsername = function(username) { return this.findOne({ username }).exec() } // verify the password of the User documment User.methods.verify = function(password) { return this.password === password } User.methods.assignAdmin = function() { this.admin = true return this.save() } module.exports = mongoose.model('User', User)7 라우터에서 방금 만든 { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 02 라우터를 불러오고, 그 라우터에 { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 03 도 적용을 해줍니다.

/routes/api/index.js

npm install --save express body-parser jsonwebtoken mongoose morgan6

코드를 저장하고, 방금 만든 API들이 잘 작동하는지 체크해봅시다.

잘 되는군요. API 요청을 할 때, npm install -g nodemon nodemon server.js3 값 설정하는것 잊지 마세요!

 

#10 비밀번호를 암호화하여 저장하기

비밀번호를 데이터베이스에 저장 할때, { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 01 그대로 저장하는것은 매우 위험합니다.  따라서 우리는 HMAC-SHA1 으로 비밀번호를 해쉬하여 저장하도록 하겠습니다.

그러기 위해선, 유저 모델에서 node.js 내장 모듈인 { "name": "nodejs-jwt-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 10 를 불러와서 사용하면 됩니다. 해싱을 할 때 사용 할 비밀키는 편의상 config 파일을 다시 불러와서 jwt 에서 사용하는 비밀키와 동일하게 사용하겠습니다.

{ (...) "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "jsonwebtoken": "^7.1.9", "mongoose": "^4.7.1", "morgan": "^1.7.0" } } 8 메소드와 npm install --save-dev eslint 0 메소드를 다음과 같이 수정해주세요.

models/user.js

npm install --save express body-parser jsonwebtoken mongoose morgan7

주의: 이전에 만든 계정들은 작동을 안할테니, 데이터베이스를 비워주시고 새로 가입을 하세요.

자, 이제 기존에 만들었던 API 들이 제대로 되는지 테스팅을 해보세요. 도중에 막히는게 있다면 소스코드를 참고해보시길 바랍니다.

 

마치면서..

이번 포스트에서는 JSON Web Token 을 사용하는 간단한 인증시스템을 구현해보았습니다. 다음 이어질 글에서는 JWT 를 사용 할 때의 보안 이슈, 토큰을 클라이언트측에서 어디에 저장해야 할 지 알아보도록 하겠습니다. 추후, React 어플리케이션에서 이 JWT 기반 인증 시스템을 연동하는 방법도 알아보겠습니다.

Toplist

최신 우편물

태그