ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [2019.05.10] JWT-Token 방식의 정보 인증 [사용법]
    개발 블로깅/기타 개념 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 관리도 더 잘 될 것 같다.

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

     

    반응형

    댓글

Designed by Tistory.