반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

오늘부터 공부한다

1. [Nodejs] Passport.js 사용하기 본문

NodeJs

1. [Nodejs] Passport.js 사용하기

1000hg 2021. 7. 15. 11:55
반응형

Passport.js는 인증 요청을 처리해주는 Node.js의 인증 미들웨어입니다.

 

기본적인 로그인 방법은 username과 password를 일치하여 확인하는 방식이지만, 최근 SNS의 증가로 Facebook과 Twitter에서 OAuth를 제공함에 따라 SNS들의 계정을 가지고 서비스를 가입하고 인증하는 방법이 생겼습니다.

 

OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보를 접근 권한으로 부여할 수 있는 개방형 표준이다

 

Passport.js는 이러한 기본적인 로그인과 OAuth 인증방법을 제공하는 패키지로, 인증 과정을 Strategy이라는 이름으로 관리합니다.

 

 

 

전체 코드는 글 가장 아래 첨부합니다.

1. Passport.js 사용법

 

기본적으로 express 프레임워크를 사용합니다.

> npm install express-generator -g 
> express -h
> express --view=ejs projectName 
> cd projectName
> npm install

 

a. view 코드

 

views/index.ejs

<!DOCTYPE html>
<html>
  <head>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1>로그인</h1>
    <form method="POST" action="/">
      	<label for="userId">아이디</label>
      	<input type="text" id="userId" name="userId"><br>
      	<label for="password">비밀번호</label>
      	<input type="password" id="password" name="password"><br>
     	<button>로그인</button>
    </form>
  </body>
</html>

 

b. 메인 코드

 

필수 모듈 설치

> npm i passport passport-local express-session connect-flash

 

passport.js

 

app.js에서 작업을 해도 상관은 없지만, 가독성을 위해 app.js와 같은 경로에 passport.js를 만들었습니다.

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

 

설정 방법입니다. 가장먼저, passport와 passport-local을 require하는 모습을 볼 수 있습니다.

 

passport-local을 선언하는 이유는 페이스북, 트위터와 같은 소셜 로그인이 아니라

local에서의 로그인 기능을 구현하기 때문에 사용합니다.

 

 

passport.use('local-login', new LocalStrategy({
    usernameField: 'userId',
    passwordField: 'password',
    passReqToCallback: true
}, (req, userId, password, done) => {
    console.log('passport의 local-login : ', userId, password)

    if(userId != 'test' || password != '12345') {
        console.log('비밀번호 불일치!')
        return done(null, false, req.flash('loginMessage', '비밀번호 불일치!'))
    }

    console.log('비밀번호 일치!')
    return done(null, {
        userId : userId,
        password: password
    })
}))

 

글의 가장 처음 부분.

기본적인 로그인 인증방법에는 username과 password의 확인, OAuth 등이 있었다고 한 것을 기억하시나요?

저희는 passport에 요청 인증을 하기 전에 어떤 인증방법을 사용할지 구성을 해야합니다.

 

passport.use(new ~strategy({}, callback)) 부분에서 저희는 그 구성을 제공할 수 있습니다.

이곳은 해당 부분은 어떤 strategy를 쓸지, 그 strategy는 어떻게 인증을 처리할지 정의하는 곳입니다.

 

단순하게 passport.use 부분에서 인증을 하고 값을 비교하여 옳고 그름을 판단하는 곳이라고만 이해해주시면 됩니다.

 

 

저희는 usernameField와 passwordField를 통해 인증을 할 것이고

(이때 usernameField와 passwordField의 value는 ejs에서 작성한 input name과 같아야합니다)

passReqToCallback을 true로 함으로써 인증을 수행하는 인증 함수로 HTTP request를 그대로 전달할지 말지에 대한 여부를 결정했습니다.

 

이제 callback 함수 부분을 알아보도록 하겠습니다.

 

저희는 현재 db를 사용하지 않고 작동원리를 파악하기 위해 하드코딩으로 test, 12345를 입력하기만 한다면 인증할 수 있도록 작성하였습니다만 저 부분은 얼마든지 바꿔줄 수 있습니다.

 

일반적으로는 데이터베이스로부터 해당 아이드의 비밀번호를 읽어와 일치여부를 판단하는 방식을 많이 사용합니다.

해당 부분은 추후 포스팅에서 볼 수 있도록 노력하겠습니다. ^^7

 

return done은 무엇인가?

 

done 함수는 자격증명을 체크하는 곳입니다.

쉽게 설명해 자격 인증을 성공, 실패에 대한 알림을 보내는 것입니다.

 

자격 증명이 유효한 경우 (성공시에)

return done(null, {인증된 값의 json})을 통해 값을 보내고

 

 

자격 증명이 유효하지 않은 경우 (ex: 암호가 잘못된 경우)

인증 실패를 나타내기 위해 json이 아닌 false를 넣어줍니다.

return done(null, false, {error message})

 

req.flash는 쉽게 말하면 에러 메시지를 보내는 것입니다.

자세한 설명은 app.js 부분에서 미들웨어를 설정할 때 설명하도록 하겠습니다.

 

 

자격 증명을 확인하는 동안의 예외 처리 (ex: DB 연결을 할 수 없을 때)

지금은 필요없는 내용이지만 db에서 값을 불러오면서 예외처리를 해야하는 경우

아래의 코드를 사용하여 예외를 처리하면 됩니다.

return done(error);

 

 

여기서 끝내고 app.js로 넘어가 미들웨어를 설정할 수 있으면 정말 좋겠지만,

아직 처리해야할 사항이 남아있습니다.

 

저희는 인증을 하고 세션에 정보를 저장하는 작업을 하지않았습니다.

passport.serializeUser(function(user, done) {
    console.log('serializeUser() 호출됨.');
    console.log(user);

    done(null, user);
});

passport.deserializeUser(function(user, done) {
    console.log('deserializeUser() 호출됨.');
    console.log(user);

    done(null, user);
})

간단한 내용입니다. 빠르게 설명하고 넘어가도록 하겠습니다.

 

로그인에 성공할 시에 serializeUser 메서드를 통해 사용자의 정보를 Session에 저장하게 됩니다.

본 예제에서는 인증 성공했을 시에 userId와 password를 보냅니다.

 

 

로그인에 성공하고 Session에 정보를 저장도 했습니다.

deserializeUser은 로그인 정보를 유지하는 작업을 맡습니다. 페이지에 접근할때 마다

사용자 정보를 Session에 가지는 역할을 맡습니다.

 

나중에 코드를 실행했을 때 해당 작동 순서를 봅시다.

 

 

 

 

 

 

app.js

다음은 프로그램의 실질적 main이라 할 수 있는 app.js를 보겠습니다.

const expressSession = require('express-session');
const passport = require('./passport.js')
const flash = require('connect-flash')

설정 방법입니다.

 

express-session은 express에서 session을 사용할 수 있도록 해주는 패키지입니다.

 

passport.js는 이전 저희가 열심히 작업한 파일입니다.

 

connect-flash는 passport.js의 done 부분 req.flash() 파라미터를 기억하신다면, 바로 아실 수 있을 것입니다.

클라이언트가 웹사이트를 이용하다 요청 처리결과를 모르거나 알수없는 오류를 마주쳐서 당황하는것을 방지하기 위해 

요청 처리 결과나 에러를 보내주는 것입니다.

 

connect-flash 미들웨어는 cookie-parser또는 express-session을 사용하므로 이들보다는 뒤로 위치해야 합니다.

 

app.use(passport.initialize())
app.use(passport.session())
app.use(flash())
app.use(expressSession({
    secret: 'my Key',
    resave: true,
    saveUninitialized:true
}))

passport.initialize()

 

Passport를 초기화하는 메서드입니다. passport는 인증을 요청하는 미들웨어라고 꾸준히 설명해왔습니다.

그렇기에 기본적으로 인증모듈을 초기화하는 작업을 가져야합니다.

 

passport.session()

 

요청 개체를 변경하고 현재 세션 ID인 'user' 값(클라이언트 쿠키에서)을 진정한 역직렬화된 사용자 개체로 변경하는 또 다른 미들웨어입니다. 쉽게 설명하면 로그인 세션을 유지하는 메서드입니다.

 

flash()connect-flash 미들웨어 기능 선언입니다.

 

 

expressSession()

 

secret : 필수항목으로 cookie-parser의 비밀키와 같은 역할resave : 요청이 왔을 때 세션에 수정사항이 생기지 않더라도 다시 저장할지에 대한 설정saveunititialized : 세션에 저장할 내역이 없더라도 세션을 저장할지에 대한 설정 (방문자 추적할때 주로 사용)cookie : 세션 쿠키에 대한 설정

 

 

미들웨어 설정이 끝났습니다.

 

마지막입니다.

router 설정만 건드려주시면 됩니다.

그 전에 성공했을때의 페이지와 실패했을 때의 페이지 2가지를 만들어줍니다.

 

views/loginFail.ejs

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>로그인 실패</title>
</head>
<body>
  <h1>로그인 실패!</h1>
</body>
</html>

views/loginSuccess.ejs

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>로그인 성공</title>
</head>
<body>
  <h1>로그인 성공!</h1>
</body>
</html>

 

routes/index.js

const express = require('express')
const router = express.Router()
const passport = require('../passport.js')
 
router.get('/', (req, res) => {
    res.render('index', {title: "인덱스"})
})
 
router.post('/', passport.authenticate('local-login', {
    successRedirect : '/loginSuccess', 
    failureRedirect : '/loginFail', 
    failureFlash : true 
}))
 
router.get('/loginSuccess', (req, res) => {
    res.render('loginSuccess')
})
router.get('/loginFail', (req, res) => {
    res.render('loginFail')
})
 
 
module.exports = router

 

특별히 설명할 내용은 passport.authenticate밖에 없습니다.

 

보통 router에서는 콜백함수를 주지만 대신 메서드를 사용하였습니다.

passport 모듈의 authenticate를 통해 인증이 성공했을때와 실패했을 때 리다이렉션할 경로를 지정합니다.

 

 

끝 ^^7

 

 

 

실행도 잘 되는 것을 알 수 있습니다.

 

 

전체 코드(ejs 파일 제외)

 

app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

const expressSession = require('express-session');
const passport = require('./passport.js');
const flash = require('connect-flash');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use(passport.initialize())
app.use(passport.session())
app.use(flash());
app.use(expressSession({
  secret: 'key',
  resave: false,
  saveUninitialized: true
}))

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

 

 

passport.js

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

passport.use('local-login', new LocalStrategy({
    usernameField: 'userId',
    passwordField: 'password',
    passReqToCallback: true
}, (req, userId, password, done) => {
    console.log('passport의 local-login : ', userId, password)

    if(userId != 'test' || password != '12345') {
        console.log('비밀번호 불일치!')
        return done(null, false, req.flash('loginMessage', '비밀번호 불일치!'))
    }

    console.log('비밀번호 일치!')
    return done(null, {
        userId : userId,
        password: password
    })
}))

passport.serializeUser(function(user, done) {
    console.log('serializeUser() 호출됨.');
    console.log(user);

    done(null, user);
});

passport.deserializeUser(function(user, done) {
    console.log('deserializeUser() 호출됨.');
    console.log(user);

    done(null, user);
})

module.exports = passport

 

routes/index.js

const express = require('express')
const router = express.Router()
const passport = require('../passport.js')
 
router.get('/', (req, res) => {
    res.render('index', {title: "인덱스"})
})
 
router.post('/', passport.authenticate('local-login', {
    successRedirect : '/loginSuccess', 
    failureRedirect : '/loginFail', 
    failureFlash : true 
}))
 
router.get('/loginSuccess', (req, res) => {
    res.render('loginSuccess')
})
router.get('/loginFail', (req, res) => {
    res.render('loginFail')
})
 
 
module.exports = router
반응형

'NodeJs' 카테고리의 다른 글

3. [Nodejs] v8엔진이란?  (1) 2021.08.10
2. [Nodejs] package.json과 package-lock.json은 무엇일까?  (0) 2021.07.22