passport 인증모듈
Passport 는 node.js 인증 범용 모듈
Http digest authentication,Oauth,OpenID
등 다양한 인증 프로토콜을 지원
-Local Strategy
Id,pw를 받아서 자체 인증 처리를 하는 가장 간단한 LocalStrategy..
Express프로젝트 생성후
웹 인증을 하기 위해서는 HTML Form 으로 부터 사용자 id와 passwd를 받기 위한 페이지를 생성하자
먼저 웹인증을 하기 위해서는 html form 으로 부터 사용자 id 와 pw 를 받기 위한 페이지를 생성하자.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action=“/login” method=“post”>
UserId:
“text” name=“userid”/>
Password:
“password” name=“password”/>
“submit” value=“Submit”/>
</form>
</body>
</html>
출처: <http://bcho.tistory.com/920>
Userid라는 필드, 상용자 pw는password
Localstrategy는 디폴트로 username 이라는 필드와 password라는 필드로 각각 로그인 id와 비밀번호를 받도록 되어 있는데 , 이 예제에서는 username 대신 앞의 폼에서 정의한 userid라는 필드로 사용자 id를 입력받도록 변경할것이다.
App.js에서 passport와 localstrategy를 사용하기 위해서 모듈을 불러들이자.
var passport = require(‘passport’)
, LocalStrategy = require(‘passport-local’).Strategy;
다음으로, passport의 LocalStrategy를 정의하고, Local Strategy에 의해서 인증시에 호출되는 인증 메서드를 정의한다..
passport.use(new LocalStrategy({
usernameField : ‘userid’,
passwordField : ‘password’,
passReqToCallback : true
}
,function(req,userid, password, done) {
if(userid==’hello’ && password==’world’){
var user = { ‘userid’:’hello’,
’email’:’hello@world.com’};
return done(null,user);
}else{
return done(null,false);
}
}
));
출처: <http://bcho.tistory.com/920>
Localstrategy객체를 생성한 후에 , 첫번째로 세팅값을 설정한다.
usernameFiled와 passwordFiled라는 인자는 localstrategy 가 html폼으로 어떤 필드를 각각 사용자 id와 pw로 읽어드릴지를 저의하는 옵션이다. 별도로 정의하지 않았을 경우 “username”과 “password”라는 필드를 사용, 여기서는 “userid”
passReqToCallback라는 옵션은 인증을 수행하는 인증함수로 Http request 를 그대로 전달할지 여부를 결정한다.
다음으로는 인증시에 호출되는 인증 함수를 정의한다. 인증함수로는 4개의 인자가 전달된다. Http request 를 전달하는 req와, 사용자 id,passwd를 전달하는 인자가 각각 들어가고 마지막으로 callback 함수인 done를 전달한다.
이 함수에서 실제로 사용자 인증을 수행
Done callback 함수에 , null과 함께, user 라는 인자를 넘기게 된다.
User 라는 인자는 객체로 사용자에 대한 로그인 정보를 저장하는 객체(이름 이나id를 저장할 수있다. )
로그인 후에는 만약 인증실패에 경우 done callback 함수에 done(null,false)로 리턴하게 되면 인증 실패 메시지를 내게 된다.
Serialize와 Deserialize
로그인이 성공하면, serializeUser 메서드를 이용하여 정보를 Session에 저장할 수있다.
passport.serializeUser(function(user, done) {
console.log(‘serialize’);
done(null, user);
});
출처: <http://bcho.tistory.com/920>
serializeUser 메서드에서는 function(user,done)을 이용해서 session에 저장할 정보를 done(null,user)과 같이 두번째 인자로 넘기면 된다. 이때 user로 넘어오는 정보는 앞의 LocalStrategy 객체의 인증함수에서 done(null,user)에 의해 리턴된 값이 넘어온다.
이 예제에서는 이 user 객체 전체를 사용자 session에 저장하였다.
다음으로, node.js의 모든 페이지에 접근할때, 로그인이 되어 있을 경우 모든 사용자 페이지를 접근할 경우 deserilizeUser가 발생한다. deserializeUser에서는 session에 저장된 값을 이용해서, 사용자 Profile을 찾은 후, HTTP Request의 리턴한다.
// 인증 후, 페이지 접근시 마다 사용자 정보를 Session에서 읽어옴.
passport.deserializeUser(function(user, done) {
//findById(id, function (err, user) {
console.log(‘deserialize’);
done(null, user);
//});
});
deserializeUser의 callback함수의 첫번째 인자로 넘어오는 내용”user”는 세션에 저장된 사용자 정보이다. 이 예제에서는 session에해당 사용자의 정보를 저장하였기 때문에 별도의 변경없이 done(null,user)를 이용해서 그대로 session에서 읽은 내용을 리턴한다.
이렇겍 리턴된 내용은 HTTP Request 에 “req.user” 값으로 다른 페이지에 전달된다.
설명이 좀 복잡한데, 배경을 설명하면 다음과 같다. Session에 사용자 정보를 저장하고자할 경우, 사용자 정보가 크다면, 메모리가 많이소모되기 때문에, serializeUser시에, 사용자 id와 같은 키 정보만 저장하도록 하고, 페이지가 접근될때 마다 deserilizeUser가 수행되면,세션에 저장된 사용자 id를 이용하여 데이타베이스에서 사용자 정보를 추가로 select해서 HTTP request에 붙여서 리턴하는 형태를 사용한다.
실제로도 PassPort의 공식 메뉴얼을 보면 다음과 같이 가이드를 하고 있다.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
serialize시에 session에서는 user.id인 사용자 id값만 저장하고, deserialize시에는 session에 저장된 id를 이용해서, DB에 매번 사용자 정보를 select하는 모습을 볼 수 있다.
그러나 이렇게 하게 되면, 실제 Session에 저장되는 데이타가 적어서 메모리는 어느 정도 절약할 수 있지만 페이지 접근시마다 매번 DB select가 발생하기 때문에 성능에 많은 저하가 오기 때문에 권장하고 싶지 않다. 아주 큰 데이타를 넣지 않는 이상 10~20개정도의 필드는 HTTP Session에 저장해도 큰 문제가 없기 때문에, 앞에서 필자가 소개한 방식대로 사용자 로그인 데이타를 모두 serialize시에session에 넣는 것을 권장한다. 만약에 데이타가 너무 많아서 메모리 사용량이 우려될 경우에는 redis와 같은 외부 메모리 DB를 이용해서 session 정보를 저장하도록 하는 방법을 사용하자.
다음으로, Express 객체를 생성한 후에, express에서 passport를 initialize하고, passport에서 session을 사용하도록 설정한다.
app.use(passport.initialize());
app.use(passport.session());
Route 모듈과 연동
이제 LocalStrategy에 대한 인증 모듈과 인증후 사용자 정보를 세션에 넣고 빼는 serilizeUser와 deserializeUser 메서드를 구현했으니 이제, login 창에서 들어오는 userid와 passwd를 가지고 인증 모듈을 실행하도록 연결을 해보자.
Passport는 connect 미들웨어의 형태로 동작한다. 특정 URL로 들어오는 log in 요청에 대해서 connect 미들웨어 형태로, passport 인증 모듈을 수행하도록 할 수 있다, Express의 route 미들웨어 등과 통합이 가능하다. passport.authenticate라는 메서드를 이용하는데,
app.get(‘/URL’,passport.authenticate(‘{strategy}’),function(req,res){
:
}
이 코드는 ‘/URL’로 들어오는 요청에 대해서 사용자 인증을 처리하도록 passport.authentication 을 중간에 미들웨어 형태로 끼워 놓은 것이다. 인증에 성공하게 되면, 뒤에 정의된 callback 함수 인 function(req,res)로 요청이 연결되고, 만약에 인증이 실패하면 401 Unauthorized HTTP response를 리턴한다. Authenticate의 첫번째 인자로 정의된 ‘{strategy}’는 passport의 인증 Strategy를 정의한다.
위의 코드에서 처럼 passport 미들웨어에서 인증이 완료된 call back function을 실행하는 방법 말고, 인증 성공/실패에 따라서 다른 페이지로 redirect할 수 있도록 설정할 수 있다.
app.post(‘/URL,passport.authenticate(‘{strategy}’,{successRedirect:’/success’
failureRedirect:’/failure’});
위의 코드는 인증이 성공했을때에는 /success URL로 리다이렉트하고, 실패했을 경우에는 /failure라는 URL로 redirect하도록 한다
그러면 두번째 소개한 redirect 방식으로, passport.authenticate 메서드를 연결해보자
app.post(‘/login’,
passport.authenticate(‘local’, { failureRedirect: ‘/login_fail’, failureFlash: true }),
function(req, res) {
res.redirect(‘/login_success’);
});
login.html에서 POST action으로 들어온 인증처리를 /login에서 하도록 하고 passport.authenticate를 ‘local’ strategy로 호출한다.
그리고 로그인이 성공했을 경우에는 ‘/login_sucess’로 redirect하고, 실패했을 경우에는 ‘/login_fail’로 redirect하도록 하였다.
페이지별로 로그인이 되었는지를 확인하고, 로그인이 되어 있을 경우 HTTP request에서 사용자 정보를 가지고 오는 코드는 다음과 같다.
app.get(‘/login_success’, ensureAuthenticated, function(req, res){
res.send(req.user);
// res.render(‘users’, { user: req.user });
});
request.user를 이용하면, deserializeUser에 의해서 저장된 사용자 정보를 꺼내볼 수 있다
Connect 미들웨어의 특성을 이용하여, 매 호출시마다 ensureAuthenticated라는 메서드를 호출하게 해서, 로그인이 되어 있는지를 확인하도록 할 수 있는데,
function ensureAuthenticated(req, res, next) {
// 로그인이 되어 있으면, 다음 파이프라인으로 진행
if (req.isAuthenticated()) { return next(); }
// 로그인이 안되어 있으면, login 페이지로 진행
res.redirect(‘/login.html’);
}
와 같이 로그인이 되어 있으면, req.isAuthenticated()가 true로 리턴된다.
지금까지 설명한 내용을 정리해보자
passport에서 인증은 express router에서 passort.authenticate 메서드를 정의함으로써 인증 메서드를 호출할 수 있다. 이때passport에서 사용한 인증 Strategy를 정의한다.
authenticate에서 정의한 인증 Strategy에 대한 개게를 passport.use를 이용해서 정의하고, 이 안에, 인증 함수를 구현한다. 이 인증함수에서는 HTML form 필드를 통해서 받은 id와 password가 전달되는데, DB등을 연동하여, 들어온 id와 passwd를 비교한후에, 성공하였을 경우 done(null,user) 정보로 리턴을 한다.
이렇게 인증 성공으로 리턴이 되면 사용자에 대한 정보를 세션에 저장할 수 있도록 passport.serailizeUser로 앞에서 done(null,user)를 통해서 전달한 user 정보가 전달된다.
그러면 passport.serializeUser에서는 전달된 user 객체중에서 세션에 저장할 정보만을 done(null,user)로 HTTP session에 저장한다.
일단 인증이 되어 로그인이된 사용자는 매 request마다 passport.desrializeUser 메서드를 호출하게 되는데, 앞에서passport.serializeUser에서 저장한 사용자 id 키를 이용해서, 사용자 정보를 DB등에서 조회하거나 하여, done(null,user)로 리턴하면, HTTP request에 함께 리턴이 된다.
해당 사용자가 로그인이되어 있는지를 확인하려면 req.isAuthenticated를 이용하여 로그인 되었는지 여부를 확인할 수 있고, 페이지별로로그인이 되어 있는지에 대해서 접근제어를 하려면 함수를 하나 정의한 후에, Express의 router에 connect 미들웨어로 전처리로 호출하게 해서 인증 여부를 확인하도록 하면 된다.
지금까지 간단하게 LocalStrategy를 통해서 Passport에 대해서 알아보았다.다음에는 Passport-facebook Strategy를 통해서 OAuth2와facebook 인증에 대해서 알아보도록 하겠다.
sample code : https://github.com/bwcho75/node.js_study/tree/master/Passport-Localstrategy