반응형
passport.js로 로그인 기능 적용하기
인증구현
app.post('/auth/login_process', //로그인 프로세스 경로로 설정
passport.authenticate('local', { successRedirect: '/', //로그인 완료 후 등장할 경로 설정
failureRedirect: '/auth/login' })); //로그인 실패 후 등장할 경로 설정
- 로그인 폼에서 전송한 데이터를 받는 쪽을 passport로 전환하기 위해서는 일단 위의 코드를 main.js에 붙여넣기 해야한다.
- 경로 또한 로그인을 할 수 있도록 하는 process 쪽으로 해줘야한다.
자격확인
main.js
//로그인 정보를 main.js로 가져옴
var authData = {
email: 'k0502s2naver.com',
password: '061599',
nickname: 'jin seok'
};
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
{
usernameField: 'email', //로그인 정보가 적힐 폼의 name 값을 원하는대로 바꿀 수 있다.
passwordField: 'pwd'
},
//아래의 콜백함수의 인자로 폼에서 전달받은 username, password가 전해진다.
function (username, password, done) {
//아이디 불일치, 비밀번호 불일치, 모두 일치 모든 상황에 if문을 이용하여 설정해두었다.
if(username === authData.email){
console.log(1); //아이디 정보가 일치시 실행
if(password === authData.password){
console.log(2); //패스워드 일치시 실행
//done의 두번째 인자에 false가 아닌 값이 들어가면 passport에서 true로 인식하고 전달한다.
return done(null, authData);
} else {
console.log(3); //패스워드 불일치시 실행
return done(null, false, {
message: 'Incorrect password.'
});
}
} else {
console.log(4); //아이디 불일치시 실행
return done(null, false, {
message: 'Incorrect username.'
});
}
}
));
세션이용
Passport.js는 내부적으로 express-session을 이용한다.
main.js
var authData = {
email: 'k0502s@naver.com',
password: '061599',
nickname: 'jin seok'
};
//1. passport.js 기본 기능과 아이디 패스워드 방식 적용하기 위한 연동 세팅
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
//2. passort.js 기본기능과 passport에서 세션기능을 사용할 수 있도록 미들웨이 적용
app.use(passport.initialize());
app.use(passport.session());
//5. 아이디 패스워드 일치 후 받은 authData 객체의 데이터를 serializeUser의 콜백함수 user 인자로 받아내고 done 메소드의
// 두번째 인자에서 아이디 값인 user.email을 자동으로 새로 생성한 세션파일에 저장시킴.
passport.serializeUser(function(user, done) {
done(null, user.email);
});
//6. deserializeUser는 로그인 완료 후의 페이지에서 어떤 작업을 하든 authData의 값을 done의 두번째
//인자로 지속적으로 호출시켜 항상 인증된 페이지 활동을 보장한다.
passport.deserializeUser(function(id, done) {
done(null, authData);
});
//4. passport가 연동되어 로그인 정보을 받은 폼에서 정보를 받은 후 아래의 코드들로
// 아이디 패스워드 일치 불일치 판단여부 결정.
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'pwd'
},
function (username, password, done) {
console.log('LocalStrategy', username, password);
if(username === authData.email){
console.log(1);
if(password === authData.password){
console.log(2);
//일치가 판단되면 done의 두번째인자에 authData 객체안의 데이터가 serializeUser의 콜백함수 user인자로 전달
return done(null, authData);
} else {
console.log(3);
return done(null, false, {
message: 'Incorrect password.'
});
}
} else {
console.log(4);
return done(null, false, {
message: 'Incorrect username.'
});
}
}
));
//3. 로그인 프로세스가 돌아갈때의 경로를 설정하여 passport에 연동하기
app.post('/auth/login_process',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/auth/login' }));
- 위의 결과처럼 로그인 하면 한 번의 serializeUser가 발동되고 페이지에 다른 활동을 할때마다 deserializeUser가 계속 발동되는 것을 터미널에서 확인할 수 있었다.
- 그리고 로그인 후 user.email의 데이터가 session데이터에 적용된 것 또한 확인 할 수 있다.
로그인 확인과 로그아웃
로그인 및 로그아웃이 된 상태를 UI에 반영하기 위하여 수정
routes/auth.js
var express = require('express');
var router = express.Router();
var path = require('path');
var fs = require('fs');
var sanitizeHtml = require('sanitize-html');
var template = require('../lib/template.js');
router.get('/login', function(request, response){
var title = 'WEB - login';
var list = template.list(request.list);
var html = template.HTML(title, list, `
<form action="/auth/login_process" method="post">
<p><input type="text" name="email" placeholder="email"></p>
<p><input type="password" name="pwd" placeholder="password"></p>
<p>
<input type="submit" value="login">
</p>
</form>
`, '');
response.send(html);
});
//로그아웃시 passport에 인해 UI가 반응하기 위해서는 main.js에서 deserializeUser의
//콜백함수의 done의 두번째 인자 user값을 여기 밑의 get 메소드의 콜백함수의 첫번째 인자
//request의 프로퍼티로서 받아낸다는 약속이 있으므로 request에 메소드 .logout을 붙여주면 passport 기능
//으로 인해 데이터들이 삭제되는 로그아웃 기능이 실행되는 것이다.
router.get('/logout', function (request, response) {
request.logout();
request.session.save(function(){
response.redirect('/');
});
});
module.exports = router;
lib/auth.js
module.exports = {
//이 또한 isOwner의 콜백함수의 인자 request에 main.js에서 deserializeUser의 콜백함수의 done의 두번째 인자 user값을
//받아냈기에 request.user라는 코드로 user 값을 가져오는 것이 가능해진 것이다.
isOwner: function(request, response){
if(request.user){ //받아온 user 값이 존재하면 true로 인식하여 if문이 실행된다.
return true;
} else{
return false;
}
}, statusUI: function(request, response){
var authStatusUI = '<a href="/auth/login">login</a>'
if(this.isOwner(request, response)){
//여기도 마찬가지로 request.user로 받은 것을 활용 시켰다.
authStatusUI = `${request.user.nickname} | <a href="/auth/logout">logout</a>`;
}
return authStatusUI;
}
}
이제 로그인이 완료되었을때 UI부분이 반응하여 닉네임과 로그인 여부를 표시하는 것을 확인 할 수 있다. 로그아웃하면 다시 UI도 로그아웃을 표시한다.
플래쉬 메세지 구현
로그인 성공 및 실패시 메세지를 등장하게 하는 기능을 구현할 수 있다. 한 번 등장하고 자동으로 사라지는 것이 특징이다.
아래 사이트에 자세한 사용법이 있다.
github.com/jaredhanson/connect-flash
$ npm install connect-flash
위 코드를 명령하여 플래시 메세지를 구현할 수 있는 프로그램 다운할 수 있다.
var flash = require('connect-flash');
app.use(flash());
app.get('/flash', function(req, res){
// Set a flash message by passing the key, followed by the value, to req.flash().
req.flash('msg', 'Flash is back!!');
res.send('flash');
});
app.get('/flash-display', function(req, res){
// Get an array of flash messages by passing the key to req.flash()
var fmsg = req.flash();
console.log(fmsg);
res.send(fmsg);
});
위 두 코드를 main.js에 붙여준다. 주의할 점은 app.use(flash()); 미들웨이는 세션에 비롯된 것이므로 세션 적용 미들웨이 밑에 붙여줘야 한다.
main.js
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require('body-parser');
var compression = require('compression');
var helmet = require('helmet')
app.use(helmet());
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var flash = require('connect-flash'); //플래시 메세지 기능 연동
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());
app.use(session({
secret: 'asadlfkj!@#!@#dfgasdg',
resave: false,
saveUninitialized: true,
store:new FileStore()
}))
app.use(flash()); //플래시 메세지 기능 미들웨이 추가
var authData = {
email: 'k0502s@naver.com',
password: '061599',
nickname: 'jin seok'
};
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log('serializeUser', user);
done(null, user.email);
});
passport.deserializeUser(function(id, done) {
console.log('deserializeUser', id);
done(null, authData);
});
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'pwd'
},
function (username, password, done) {
console.log('LocalStrategy', username, password);
if(username === authData.email){
console.log(1);
if(password === authData.password){
console.log(2);
return done(null, authData, {message: 'Welcome'}); //로그인 성공시 메세지 추가. success 객체에 담길 예정
} else {
console.log(3);
return done(null, false, {
message: 'Incorrect password.' //로그인 실패시 메세지이며 error 객체에 담길 예정
});
}
} else {
console.log(4);
return done(null, false, {
message: 'Incorrect username.' //로그인 실패시 메세지이며 error 객체에 담길 예정
});
}
}
));
app.post('/auth/login_process',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/auth/login' ,
failureFlash:true, //로그인 실패시 플래시 기능 true로 설정하여 스위치 온
successFlash:true}));// 로그인 성공시 플래시 기능 ture로 설정하여 스위치 온
routes/auth.js
var express = require('express');
var router = express.Router();
var path = require('path');
var fs = require('fs');
var sanitizeHtml = require('sanitize-html');
var template = require('../lib/template.js');
router.get('/login', function(request, response){
//아래와 같이 request을 통해 flash() 기능을 가져와 feedback 변수에 error 객체의 첫번째 프로퍼티 값 삽입
// error는 객체이며 플래시 기능 발동시 생성된다. 그리고 한 번만 프로퍼티 값이 등장하는 1회용이다.
var fmsg = request.flash();
var feedback = '';
if(fmsg.error){
feedback = fmsg.error[0];
}
var title = 'WEB - login';
var list = template.list(request.list);
var html = template.HTML(title, list, `
<div style="color:red;">${feedback}</div> //로그인 실패시 해당 UI 등장토록 함.
<form action="/auth/login_process" method="post">
<p><input type="text" name="email" placeholder="email"></p>
<p><input type="password" name="pwd" placeholder="password"></p>
<p>
<input type="submit" value="login">
</p>
</form>
`, '');
response.send(html);
});
router.get('/logout', function (request, response) {
request.logout();
request.session.save(function(){
response.redirect('/');
});
});
module.exports = router;
routes/index.js
var express = require('express');
var router = express.Router();
var template = require('../lib/template.js');
var auth = require('../lib/auth');
router.get('/', function(request, response) {
//마찬가지로 request을 통해 플래시 기능 가져와서 feedback에 success 객체의 첫번째 값 삽입
var fmsg = request.flash();
var feedback = '';
if(fmsg.success){
feedback = fmsg.success[0];
}
var title = 'Welcome';
var description = 'Hello, Node.js';
var list = template.list(request.list);
var html = template.HTML(title, list,
`<div style="color:blue;">${feedback}</div>
<h2>${title}</h2>${description}
<img src="/images/BR.jpg" style="width:300px; display:block; margin-top:10px;">
`,
`<a href="/topic/create">create</a>`,
auth.statusUI(request, response)
);
response.send(html);
});
module.exports = router;
위와 같이 로그인 성공시와 실패시 플래시 메세지가 등장하는 것을 확인 할 수 있었다.
최종 리팩토링 정리
lib/passport.js
//exports을 이용하여 외부인 main.js에서의 'app'을 쓰기 위하여 'app'을 매개변수로 가져와 함수로 묶어주었다.
module.exports = function (app) {
var authData = {
email: 'k0502s@naver.com',
password: '061599',
nickname: 'jin seok'
};
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function (user, done) {
done(null, user.email);
});
passport.deserializeUser(function (id, done) {
done(null, authData);
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'pwd'
},
function (username, password, done) {
if (username === authData.email) {
if (password === authData.password) {
return done(null, authData, {
message: 'Welcome.'
});
} else {
return done(null, false, {
message: 'Incorrect password.'
});
}
} else {
return done(null, false, {
message: 'Incorrect username.'
});
}
}
));
return passport;
}
main.js
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require('body-parser');
var compression = require('compression');
var helmet = require('helmet')
app.use(helmet());
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var flash = require('connect-flash');
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());
app.use(session({
secret: 'asadlfkj!@#!@#dfgasdg',
resave: false,
saveUninitialized: true,
store:new FileStore()
}))
app.use(flash());
//외부에 있는 passport 기능을 구현하는 코드들을 모아둔 lib/passport.js에 app을 매개변수로 보내줌.
var passport = require('./lib/passport')(app);
app.get('*', function(request, response, next){
fs.readdir('./data', function(error, filelist){
request.list = filelist;
next();
});
});
var indexRouter = require('./routes/index');
var topicRouter = require('./routes/topic');
//이 또한 마찬가지로 passport 기능 코드를 가지고 있는 auth.js에 'passport'을 매개변수로 보내줌.
var authRouter = require('./routes/auth')(passport);
app.use('/', indexRouter);
app.use('/topic', topicRouter);
app.use('/auth', authRouter);
app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!')
});
routes/auth.js
var express = require('express');
var router = express.Router();
var path = require('path');
var fs = require('fs');
var sanitizeHtml = require('sanitize-html');
var template = require('../lib/template.js');
//외부인 main.js로부터 exports을 이용하여 'passport' 받아와 매개변수로 사용하여 함수 묶어줌.
module.exports = function (passport) {
router.get('/login', function (request, response) {
var fmsg = request.flash();
var feedback = '';
if (fmsg.error) {
feedback = fmsg.error[0];
}
var title = 'WEB - login';
var list = template.list(request.list);
var html = template.HTML(title, list, `
<div style="color:red;">${feedback}</div>
<form action="/auth/login_process" method="post">
<p><input type="text" name="email" placeholder="email"></p>
<p><input type="password" name="pwd" placeholder="password"></p>
<p>
<input type="submit" value="login">
</p>
</form>
`, '');
response.send(html);
});
router.post('/login_process',
//여기서 passport 기능을 사용함
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/auth/login',
failureFlash: true,
successFlash: true
}));
router.get('/logout', function (request, response) {
request.logout();
request.session.save(function () {
response.redirect('/');
});
});
return router;
}
반응형
'프로그래밍 개발 > Express' 카테고리의 다른 글
Express - 비밀번호 암호화 (0) | 2021.01.19 |
---|---|
Express - 다중 사용자 구현(회원가입) (0) | 2021.01.19 |
Express -passport.js 설치하기 (0) | 2021.01.18 |
Express - session을 이용한 로그인 인증 구현 (0) | 2021.01.16 |
Express - express-session의 기본 개념 (0) | 2021.01.16 |
댓글