개발 블로깅/기타 개념

[2019.05.10] JWT-Token 방식의 정보 인증 [사용법]

Hello이뇽 2019. 5. 10. 22:24

사용자가 로그인 후, 유저 정보를 cookie,Session 방식이 아닌 token 방식으로 안전하게 관리하는 방법이 있다.

# token 방식의 특징

  • 사용자 정보를 일일히 서버의 세션에 저장하지 않고, 사용자의 로컬에 저장.
  • 사용자가 요청을 보낼 때마다 유저 정보 확인을 일일히 하지 않아도 됨.
  • 웹 표준 기반 기술로써, 여러 환경에서 지원

이러한 토큰 방식을 사용하기 위해서는 JWT(Json Web Token) 기술을 사용한다.

 


# JWT 모듈 설치법

npm i -D jsonwebtoken

모듈을 설치한다.


# token 생성법

토큰을 생성하는 방법은 아래와 같다. 

const jwt = require('jsonwebtoken');

var userInfo = {id: 1, username: 'inyong'};
var secretKey = 'SeCrEtKeYfOrHaShInG';
var options = {expiresIn: '7d', issuer: 'inyongTest', subject: 'userInfo'};

jwt.sign(userInfo, secretKey, options, 
    function(err,token){
      if(err) console.log(err);
      else console.log(token); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJpbnlvbmciLCJpYXQiOjE1NTc0ODc2MjIsImV4cCI6MTU1ODA5MjQyMiwiaXNzIjoiaW55b25nVGVzdCIsInN1YiI6InVzZXJJbmZvIn0.idVKe2FVsmvwYBPFJc9vMXi4eZRfFJ6rwhiHIY4gZeo
    }
)

 

jwt.sign() 함수에 들어가는 4가지 인자 설명

  • userInfo : 아이디, 비밀번호 등 사용자 정보가 들어간 object이다. 형식은 상관없음.
  • secretKey : 여러가지 복잡한 문자열로 되어있는 키.
  • options: 토큰에 대한 여러가지 정보를 설정한다. expiresIn은 토큰 만료일, issuer, subject는 토큰에 대한 정보이다. 외에도 options가 더 있다.
  • 4번째 인자로 들어가는 익명함수 : token 생성결과를 4번째 인자의 콜백함수로 받을 수 있으므로 넣어준 함수.

 

그럼 서버에서 사용자에게 요청을 받았을 시, token을 사용자에게 전달해주는 코드를 작성해보자.

예시)

const express = require("express");
const jwt = require('jsonwebtoken');
const app = express();

app.get('/',(req, res)=>{
  const getToken = () => {
    return new Promise((resolve, reject) => {
      jwt.sign(
        {
          id: 1,
          username: 'inyong'     // 유저 정보
        },
        
        'SeCrEtKeYfOrHaShInG',   // secrec Key
        
        {
          expiresIn: '7d',
          issuer: 'inyongTest',   // options
          subject: 'userInfo'
        }, 
        
        function(err,token){
          if(err) reject(err)      // callback
          else resolve(token)
        }
      )
    });
  } 
  
  getToken().then(token =>{
    res.send(token); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJpbnlvbmciLCJpYXQiOjE1NTc0ODc2MjIsImV4cCI6MTU1ODA5MjQyMiwiaXNzIjoiaW55b25nVGVzdCIsInN1YiI6InVzZXJJbmZvIn0.idVKe2FVsmvwYBPFJc9vMXi4eZRfFJ6rwhiHIY4gZeo
  })
});

var server = app.listen(3000, function(){
  console.log("Express server has started on port 3000");
});

 

jwt.sign()함수는 비동기 함수이므로 Promise 처리를 해줘야 한다. 하단 부분에 res.send()로 사용자에게 토큰을 보내준다.

postMan을 이용해서 서버에게 요청을 보내보고, 응답으로 받은 토큰 값을 확인해보자.

 

JWT 홈페이지에서, 서버에게 받은 token값을 이용하여 인코딩 및 디코딩을 해볼 수 있다. token으로 나온 값을 Encoded 입력박스에 넣어보면, sign으로 넣었던 user 정보 및 options가 나오는 것을 확인할 수 있다.


# token 검증 방법

사용자는 서버에게서 토큰을 받은 후, 서버에게 요청을 보낼 때, request.Header에 토큰을 포함하여 요청을 보낸다.

그러면 서버는 사용자에게서 받은 토큰이 유효한 것인지 확인한다.

const secretKey = ''; // 아까 token 만들때 썼던 secretkey
const router = (req, res) => {
  const token = req.headers['x-access-token'] || req.query.token;
  jwt.verify(token, secretKey, 
    function(err, decoded){
      console.log(err) // 유효하지 않은 토큰
      console.log(decoded) // 유효한 토큰, 유저 정보 Object 반환
    }
}

jwt.verify()함수를 이용하여 토큰 유효성을 확인할 수 있다.

jwt.verify() 함수에 들어가는 매개변수 3개

  • token: client에게서 받은 token
  • secretkey : token 생성 시 사용했던 secretKey
  • 3번째 인자로 들어간 익명함수 : 유효성 검사 결과를 처리할 callback 함수

예시)

client

var token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJpbnlvbmciLCJpYXQiOjE1NTc0OTE3NTYsImV4cCI6MTU1ODA5NjU1NiwiaXNzIjoiaW55b25nVGVzdCIsInN1YiI6InVzZXJJbmZvIn0.fpLkn6R6zjJXFq0i9xWpSmLrYmo-afyBjbPg2fJM25s';

fetch('http://localhost:3000', {
      method: 'POST',
      body: JSON.stringify({name:'inyong'}),
      headers: {
        'content-type': 'text/json',
        'x-access-token': token
      }
})
.then(res => {return res.json()})
.then(res => {
  this.render(res);
});
      
      // client 단 서버 요청

 

server

//생략
...

const router = (req, res, next) => {
  const token = req.headers['x-access-token'] || req.query.token;  // client에게서 받은 토큰
  
  /* 토큰이 없으면 403 에러 응답 처리 */
  if(!token){
    return res.status(403).json({  
      success: false,
      message: 'not logged in'
    });
  }
  /* 토큰 유효성 검사 */
  const p = new Promise((resolve, reject) => {
    jwt.verify(token, req.app.get('jwt-secret'), (err,decoded) => {
      if(err) reject(err);
      else resolve(decoded);
    })
  });
  /* 유효하지 않은 토큰으로 403 에러 처리 */
  const onError = (error) => {
    res.status(403).json({
      success: false,
      message: error.message
    })
  };

  p.then((decoded)=>{
    res.json(decoded);
  }).catch(onError);
}

app.post('/decoded', router);

참고로  secret 키와 토큰 검증 부분은 요청시 계속 사용되는 부분이다.

secret키는 .config파일로 따로 빼고, 토큰 검증 함수는 미들웨어로 빼서 사용하면 쉽게 사용할 수 있다.

 

앞으로 정보 관리방식은 JWT방식으로 처리하는 방식으로 쓰면 될 것 같다.

cookie와 Session 방식의 로그인 정보 인증을 해보지는 않았지만, 굳이 JWT 방식을 놔두고 써 볼 일은 없을 것 같다. 확실히 JWT 방식의 장점이 더 많고, Session 관리도 더 잘 될 것 같다.

벨로퍼트님 강의가 정말 많은 도움이 되고 있다....

 

반응형