Javascript/Node.js

Express Session& Auth - session 미들웨어, 인증 구현

leeeee.yeon 2021. 7. 10. 23:01

쿠키는 사용자를 식별하는데만 사용하고, 실제 데이터는 서버 쪽에 안전하게 저장하는게 session 방법이다.

 

session 미들웨어를 사용할 것이다.

npm install -s express-session

 

간단한 예제로 session에 대해 알아보자.

const express = require('express');
const app = express();

var session = require('express-session')

app.use(session({
	secret: 'keyboard cat',
    resave: false,
    saveUninitialize: true
}));

app.get('/', function(req, res, next){
	res.send('Hello session!');
});

app.listen(3000, function(){
	console.log('3000!');
});
  • express-session 모듈을 불러온다.
  • app.use() - 사용자의 요청이 있을 때마다 코드를 실행 > session 함수를 실행하여 session이 시작됨
  • secret : 필수 옵션, 노출되면 안됨
  • resave : false를 디폴트로 하자, 세션 데이터가 바뀌기 전에는 값을 저장소에 저장하지 않는다.
  • saveUninitialized : true를 디폴트로 하자, 세션이 필요하기 전까지는 세션을 구동시키지 않는다.

 

session 미들웨어는 request의 session이라는 property로 추가시켜준다.

session 저장소에 저장했다가 요청하면 다시 공급한다.

여러가지 session store이 있는데 강의에서는 가장 간단한 file을 사용한다.

npm install -s session-file-store
const express = require('express');
const app = express();

var session = require('express-session');
var FileStore = require('session-file-store')(session)

app.use(session({
	secret: 'keyboard cat',
    resave: false,
    saveUninitialize: true,
    store:new FileStore()
}));

app.get('/', function(req, res, next){
	res.send('Hello session!');
});

app.listen(3000, function(){
	console.log('3000!');
});

session 미들웨어는 쿠키로 전달된 session id 값을 읽어 session store 안에서 대응되는 파일을 읽어 request.session의 property에 추가하여 웹 페이지에 공급하여 준다.

 

mysql을 session store로 사용하는 미들웨어가 따로 있으니 그것을 사용하자!

이제까지는 강의에 나온 그대로 했는데 앞으로는 강의에 나온 내용을 응용해서 코딩을 해야 한다 ....! 두근두근 (●ˇ∀ˇ●)

하려고 했는데 ...! 여기서 MySQL을 도입하면 후에 passport.js나 다중 사용자 공부하면서 복잡해질거 같아

일단 node.js 관련 강의를 쭈욱 다 듣고 그 후에 MySQL을 스스로 도입하기로 했다!

 


UI는 아까 Node.js 쿠키 강의를 들으면서 구현하였으니 그대로 사용한다.

려고 했는데 조금씩 다르다 !!!!

 

일단 main.js에

const authRouter = require('./routes/auth.js');
app.use('/auth', authRouter);

를 추가해주고 template.js의 HTML 부분을 아래와 같이 바꾼다.

HTML:function(title, list, body, control){
    return `
    <!doctype html>
    <html>
    <head>
      <title>WEB1 - ${title}</title>
      <meta charset="utf-8">
    </head>
    <body>
      <a href="/auth/login">login</a>
      <h1><a href="/">WEB</a></h1>
      <a href="/author">author</a>
      ${list}
      ${control}
      ${body}
    </body>
    </html>
    `;
}

 

UI 구현까지 완료한 auth.js

const express = require('express');
const router = express.Router();

const sanitizeHtml = require('sanitize-html');

// 파일 분리
const db = require('../lib/db.js');
const template = require('../lib/template.js');

router.get('/login', function(request, response){
  db.query(`SELECT * FROM topic`, function(error, topics){
    db.query(`SELECT * FROM author`, function(error2, authors){

      var title = 'Login';
      var list = template.list(topics);
      var html = template.HTML(sanitizeHtml(title), list,
        `
        <form action="/auth/login_process" method="post">
          <p><input type="text" name="id" placeholder="id"></p>
          <p><input type="password" name="password" placeholder="password"></p>
          <p>
            <input type="submit" value="login">
          </p>
        </form>
      `,
        ``
      );

      response.send(html);
    });
  });
});

module.exports = router;

 

세션 미들웨어를 사용해보자.

 

main.js에 아래 코드를 추가해준다.

const session = require('express-session');
const fileStore = require('session-file-store')(session);

app.use(session({
    secret: 'qwertyasdfg',
    resave: 'false',
    saveUninitialized: true,
    store: new fileStore()
}));

 

그리고 auth.js에서 login_process 부분을 아래와 같이 수정하면

router.post('/login_process', function(request, response){
  var post = request.body;
  var id = post.id;
  var pwd = post.password;

  if(id===authData.id && pwd===authData.password){
      request.session.is_logined = true;
      request.session.nickname = authData.nickname;
      response.redirect(`/`);
  } else{
      response.send('Who?');
  }
});

 

sessions 디렉토리가 생기고, 로그인을 하면 session 객체가 만들어진 것을 확인할 수 있다.

 

이제까지 로그인 기능을 구현하고, 로그인 시 홈 페이지로 리다이렉트하는 것까지 구현했다. 그러나 현재 상태로는 로그인 여부를 확인할 수 없다.

그러니 이번에는 인증 상태를 UI에 반영해보자.

 

home.js와 template.js를 수정해주어야 한다.

 

< home.js >

function authIsLogin(request, response){
    if(request.session.is_logined){
        return true;
    } else{
        return false;
    }
}

function authStatusUI(request, response){
    var authStatusUI = '<a href="/auth/login">login</a>';
    if(authIsLogin(request, response)){
        authStatusUI = `${request.session.nickname} | <a href="/auth/logout">logout</a>`;
    }

    return authStatusUI;
}

router.get('/', function(request, response){
    db.query(`SELECT * FROM topic`, function(error, topics){
      if(error) throw error;
      var title = 'Welcome';
      var description = 'Hello, Node.js';
      var list = template.list(topics);
      var html = template.HTML(title, list,
        `
        <h2>${title}</h2>${description}
        <img src="/images/squirrel.jpg" style="width: 300px; display: block; margin-top" 10px">
        `,
        `<a href="/topic/create">create</a>`,
        authStatusUI(request, response)
      );
      response.send(html);
    });
  });
  • authIsLogin() 함수를 만들고,
  • authStatusUI() 함수를 제작하고,
  • router.get 안에서 authStatusUI() 함수를 사용했다.

 

< template.js > - HTML 부분

  HTML:function(title, list, body, control, authStatusUI='<a href="/auth/login">login</a>'){
    return `
    <!doctype html>
    <html>
    <head>
      <title>WEB1 - ${title}</title>
      <meta charset="utf-8">
    </head>
    <body>
      ${authStatusUI}
      <h1><a href="/">WEB</a></h1>
      <a href="/author">author</a>
      ${list}
      ${control}
      ${body}
    </body>
    </html>
    `;
  }

 

 

이제 이거를 author, create, update 등 다른 페이지에서도 나타나도록 하자.

 

lib 디렉토리에 auth.js 파일을 만들고, 로그인 관련 함수를 다른 파일에서도 사용할 수 있도록 하자.

module.exports = {
    isLogin: function(request, response){ // 로그인 여부 확인
        if(request.session.is_logined){
            return true;
        } else{
            return false;
        }
    },
    statusUI: function(request, response){
        var authStatusUI = '<a href="/auth/login">login</a>';
        if(this.isLogin(request, response)){
            authStatusUI = `${request.session.nickname} | <a href="/auth/logout">logout</a>`;
        }
    
        return authStatusUI;
    }
}

각 파일(topic.js, author.js)에서 lib/auth.js를 불러오고, template.html() 함수를 수정해주자.

const auth = require('../lib/auth.js');

 

여기까지 로그인 구현 완료 !

로그인을 구현했으니 로그아웃을 구현하자.

 

routes 디렉토리의 auth.js 파일에 logout을 다루는 router.get()을 추가하자.

router.get('/logout', function(request, response){
      request.session.destroy(function(err){
        response.redirect('/');
      });
});
  • session의 destroy 메서드를 이용해서 세션을 삭제할 수 있다.

헉 근데 로그아웃 이후 다시 로그인하면 로그인이 되지 않는다 ...!

그리고 재 로그인시 만들어진 세션을 봤는데 로그인 관련 정보가 저장되어 있지 않다 ...!

이 부분을 고쳐야할듯

이라고 생각했는데 후속 강의(세션 저장)를 듣고 해결했다.

 

[ 세션 저장 ]

 

데이터를 세션 스토어에 기록 (= 메모리에 저장된 세션 데이터를 저장소에 반영)

그러나 저장소가 느려졌을 때 세션 스토어 저장은 안됐는데 리다이렉트는 끝나는 현상이 발생할 수 있다.

그럴 때를 위해 save 메서드를 활용할 수 있다. > 세션 스토어에 기록을 바로 시작하고, 기록이 됐을 때 리다이렉트를 실행한다.

router.post('/login_process', function(request, response){
  var post = request.body;
  var id = post.id;
  var pwd = post.password;

  if(id===authData.id && pwd===authData.password){
      request.session.is_logined = true;
      request.session.nickname = authData.nickname;
      request.session.save(function(){
        response.redirect(`/`);
      });
  } else{
      response.send('Who?');
  }
});

 

마지막으로,

if(!auth.isLogin(request, response)){
  response.redirect('/');
  return false;
}

이 코드를 CRUD가 들어가는 곳들에 추가하여 로그인 되지 않았을 시 CRUD에 접근하지 못하도록 한다.

 

이로써 leeeeeyeon이라는 사용자(단일)에 대한 인증 구현을 마쳤다 !!!

 


[ 수업을 마치며 ]

 

- https 통신을 하도록 하자.

app.use(session({
    secure: true,
    secret: 'qwertyasdfg',
    resave: 'false',
    saveUninitialized: true,
    store: new fileStore()
}));
  • secure: true 추가 > https에서만 세션을 주고 받음 ( 참고로 localhost는 http )

- 해킹을 막기 위해 사용자가 입력하는 정보에 자바스크립트를 사용할 수 없도록 하자. > httpOnly 사용

- 다중 사용자를 수용할 수 있는 기능을 추가하자

- 소셜 로그인 기능(oauth, passport.js)을 추가하자. > 자사 사이트에선 회원 식별만 하고 회원 정보 관리는 타사에 맡김

 

강의를 들으며 점점 백엔드에 익숙해지고, 지식이 늘어나는거 같아 뿌듯하다 !!

그리고 다중 사용자 강의에 SQL 연동 부분이 있는걸 발견했다 !

lowdb라는 것을 이용해 MySQL과 사뭇 다르지만, 그래도 참고할만한 소스가 생겨서 좋다 ㅎㅅㅎ