본문 바로가기

STUDY/Node.js

Node.js | Passport.js (passport-local) + MySQL

Node.js 인증 미들웨어인 Passport.js는 Node.js의 프레임워크인 express를 기반으로 합니다.

express관련 이전 글을 참고하세요!

2019/12/18 - [STUDY/Node.js] - Node.js | Express 사용하기 (기본 Routing)

2019/12/23 - [STUDY/Node.js] - Node.js | Express application generator

 

Passport.js

Simple, unobtrusive authentication for Node.js

www.passportjs.org

 

1. 설치

 

npm을 이용하여 passport.js를 사용하기 위해 passport를 설치하고,

$ npm install passport

 

ID(username)와 비밀번호를 이용하는 가장 기본적인 인증방식을 Strategy로 사용하는 passport-local모듈도 설치합니다.

$ npm install passport-local

 

 

 

2. 사용준비

 

passport를 사용할 때 반드시 필요한 미들웨어들을 import 해줍니다.

 

  • express : passport.js는 express를 기반으로 작동하기 때문에 반드시 필요합니다.
  • body-parser : 브라우저에서 입력받은 정보에 접근할 수 있습니다.
  • cookie-parser, express-session : passport는 인증이 실행된 뒤 유저의 정보를 express-session을 이용하여 저장합니다.
  • passport, passport-local : 당연히 passport모듈 또한 필요합니다. strategy는 알맞게 불러오시면 됩니다.
  • 이 밖에 자신이 사용하는 데이터베이스를 사용하기 위한 모듈도 import해주세요.
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const passport = require('passport')
  , LocalStrategy = require('passport-local').Strategy;

 

그리고 미들웨어들을 사용하기 위한 준비(configuration)가 필요합니다. 이 때의 순서가 중요합니다.

반드시 session이후에 passport.initialize()와 passport.session()이 위치해야 합니다.

router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: false }));
router.use(cookieParser('keyboard cat'));
router.use(session({secret: 'keyboard cat'}));
router.use(passport.initialize());
router.use(passport.session());

 

이제 passport를 사용할 수 있습니다.

 

 

 

 

 

 

3. 로그인 기능 구현해보기(+MySQL)

 

로그인 form을 작성합니다.

passport.js의 공식 문서에서 기본적으로 HTML태그를 이렇게 작성하길 권장합니다.
name값을 위의 태그처럼 작성하면 자동적으로 passport에서 인식하여 파라미터값으로 받아줍니다.
(하지만 저는 이미 사용하던 form 태그가 있었기 때문에 변경하지 않고 사용하겠습니다.)

 

제가 사용한 로그인form 태그입니다.

 <form method="post" action="/loginAf">
   ID <input type="text" name="id" value=<%= userId %>><br>
   PASSWORD <input type="text" name="pwd"><br>
   ID 저장<input type="checkbox" value="checked" name="rememberId"><br>
   <input type="submit" value="LOGIN">
 </form>

 

 

 

passport.js의 공식 사이트에서 알려주는 route작성법입니다.

이 코드를 자신의 상황과 맞게 수정해주면 됩니다.

 

 

 

저는 이렇게 수정했습니다.

router.post('/loginAf',
  passport.authenticate('local', { successRedirect: '/home',
                                   failureRedirect: '/login',
                                   failureFlash: true })
);

 

 

 

로그인 성공여부 판단

done을 통해 로그인 성공여부를 판단하여 route에서 success / fail 구분이 가능해집니다.

 

위의 메서드를 살펴보면 LocalStratey를 사용할 때 파라미터 값으로 username과 password를 받아주고 있습니다.

하지만 저는 id와 pwd라는 값으로 데이터를 전달받기 때문에 파라미터값을 잘 찾아올 수 있도록 파라미터 등록을 해주어야 합니다.

 

 

함수가 실행되기 이전에 파라미터를 변경해주면 됩니다.

 

이렇게 수정해주면 id는 username으로, pwd는 password로 전달됩니다.

passport.use(new LocalStrategy({
    usernameField: 'id',
    passwordField: 'pwd'
  },
  function(username, password, done) {
    
    })
  }
));

 

 

 

DB와 소통하기

이제 받아온 ID와 비밀번호를 이용하여 로그인 여부를 판단해주면 됩니다.

MySQL과 Node.js연동은 이전글을 참고하세요.

 

로그인에 실패한 경우 false를,

로그인에 성공한 경우 true는 생략이 가능하며 DB에서 받아온 회원 정보 객체를 return해줍니다.

이제 req.user를 통해 현재 회원정보 객체에 접근이 가능합니다.

passport를 사용하지 않았을 때 로그인 한 id를 가져오려면 req.session.user_id로 가져왔다면,

passport를 사용할 때는 req.user.id로 가져올 수 있습니다.

passport.use(new LocalStrategy({
    usernameField: 'id',
    passwordField: 'pwd'
  },
  function(username, password, done) {
    var sql = 'SELECT * FROM USER WHERE ID=? AND PWD=?';
    mysql.query(sql , [username, password], function (err, result) {
      if(err) console.log('mysql 에러');  

      // 입력받은 ID와 비밀번호에 일치하는 회원정보가 없는 경우   
      if(result.length === 0){
        console.log("결과 없음");
        return done(null, false, { message: 'Incorrect' });
      }else{
        console.log(result);
        var json = JSON.stringify(result[0]);
        var userinfo = JSON.parse(json);
        console.log("userinfo " + userinfo);
        return done(null, userinfo);  // result값으로 받아진 회원정보를 return해줌
      }
    })
  }
));

 

 

return받은 회원정보는 세션에 저장되는데, 이 세션을 정의하는 태그가 필요합니다.

serializeUser를 통해 user정보를 식별할 수 있는 값인 id를 이용한 쿠키를 생성하고,

deserializeUser를 통해 id를 이용해 user의 전체 정보를 다시 받아올 수 있습니다(?)

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

 

 

deserializeUser에서 실행되는 콜백함수의 인자값인 id를 이용하여 DB에서 SELECT해줍니다.

이 코드는 로그인이 된 후 계속되는 후속요청에서 실행되기 때문에 id만 있으면 되는듯..합니다...

passport.serializeUser(function(user, done) {
  console.log("serializeUser ", user)
  done(null, user.ID);
});

passport.deserializeUser(function(id, done) {
    console.log("deserializeUser id ", id)
    var userinfo;
    var sql = 'SELECT * FROM USER WHERE ID=?';
    mysql.query(sql , [id], function (err, result) {
      if(err) console.log('mysql 에러');     
     
      console.log("deserializeUser mysql result : " , result);
      var json = JSON.stringify(result[0]);
      userinfo = JSON.parse(json);
      done(null, userinfo);
    })    
});

 

 

 

 

+) 전체 코드 (순서를 보세요!)

var express = require('express');
var mysql = require('../mysql.js');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var router = express.Router();
var passport = require('passport')
  , LocalStrategy = require('passport-local').Strategy;

//세션은 가장 상위 route에서 생성해주었습니다.
router.use(bodyParser.urlencoded({ extended: false }));
router.use(cookieParser());
router.use(passport.initialize());
router.use(passport.session());

passport.serializeUser(function(user, done) {
  console.log("serializeUser ", user)
  done(null, user.ID);
});

passport.deserializeUser(function(id, done) {
    console.log("deserializeUser id ", id)
    var userinfo;
    var sql = 'SELECT * FROM USER WHERE ID=?';
    mysql.query(sql , [id], function (err, result) {
      if(err) console.log('mysql 에러');     
     
      console.log("deserializeUser mysql result : " , result);
      var json = JSON.stringify(result[0]);
      userinfo = JSON.parse(json);
      done(null, userinfo);
    })    
});

router.get('/login', function(req, res, next) {
  var userId = "";
  if(req.cookies['loginId'] !== undefined){
    console.log(req.cookies['loginId']);
    userId = req.cookies['rememberId'];
  }
  res.render('login', {userId: userId});
});

passport.use(new LocalStrategy({
    usernameField: 'id',
    passwordField: 'pwd'
  },
  function(username, password, done) {
    var sql = 'SELECT * FROM USER WHERE ID=? AND PWD=?';
    mysql.query(sql , [username, password], function (err, result) {
      if(err) console.log('mysql 에러');  

      // 입력받은 ID와 비밀번호에 일치하는 회원정보가 없는 경우   
      if(result.length === 0){
        console.log("결과 없음");
        return done(null, false, { message: 'Incorrect' });
      }else{
        console.log(result);
        var json = JSON.stringify(result[0]);
        var userinfo = JSON.parse(json);
        console.log("userinfo " + userinfo);
        return done(null, userinfo);  // result값으로 받아진 회원정보를 return해줌
      }
    })
  }
));

router.get('/home', function (req, res, next) {
  res.render('home', {"user_id" : req.user.ID});
});

router.post('/loginAf',
  passport.authenticate('local', { successRedirect: '/home',
                                   failureRedirect: '/login',
                                   failureFlash: true })
);

module.exports = router;

 

 

 

더보기

DB정보

 

 

+) 참고

 

Documentation

Overview Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When writing modules, encapsulation is a virtue, so Passport delegates all other functionality to the application. This separation o

www.passportjs.org