-
15장 passport 모듈을 이용한 사용자 인증 (2)MeanStack (deprecated) 2016. 6. 26. 01:18
저번 장에 이어서 이제는 실제로 passport 모듈을 사용할 인증페이지 즉, 가입화면과 로그인페이지를 만들 것 이다.
먼저 app/views 폴더로 가서 signup.ejs 라는 가입화면을 만들어보자.
<!DOCTYPE html> <html> <head> <title> <%= title %> </title> </head> <body> <% for(var i in messages) { %> <div class="flash"><%= messages[i] %></div> <% } %> <form action="signup" method="post"> <div> <label>Username : </label> <input type="text" name="username"> </div> <div> <label>Email : </label> <input type="text" name="email"> </div> <div> <label>UserID : </label> <input type="text" name="userid"> </div> <div> <label>Password : </label> <input type="password" name="password"> </div> <div> <input type="submit" value="Sign up"> </div> </form> </body> </html>
signup.ejs 뷰는 단순히 title 변수를 출력하는 ejs 태그와 messages 리스트 변수를 출력하는 EJS 루프를 포함한다.
그리고 바로 로그인 화면을 이어서 만들어보자. app/views 폴더로 가서 signin.ejs 라는 파일을 만들자 .
<!DOCTYPE html> <html> <head> <title> <%= title %> </title> </head> <body> <% for(var i in messages) { %> <div class="flash"><%= messages[i] %></div> <% } %> <form action="signin" method="post"> <div> <label>UserID : </label> <input type="text" name="username"> </div> <div> <label>Password : </label> <input type="password" name="password"> </div> <div> <input type="submit" value="Sign up"> </div> </form> </body> </html>
Signin 은 더욱 단순하다. Signup 과 같이 타이틀과 메시지를 출력하는 부분과 아이디, 패스워드를 받는 부분이
끝이다. 이제 모델과 뷰과 준비 되었으므로 User 컨트롤러를 사용해 둘을 연결해보자.
app/controllers/ 폴더로 가서 users.server.controller.js 파일을 변경하자.
var User = require('mongoose').model('User'), passport = require('passport'); var getErrorMessage = function(err) { var message = ''; if(err.code) { switch (err.code) { case 11000: case 11001: message = 'UserID already exists'; break; default: message = 'Something went Wrong'; } } else { for (var errName in err.errors) { if(err.errors[errName].message) message = err.errors[errName]. message; } } return message; }; exports.renderSignin = function(req, res, next) { if(!req.user) { res.render('signin', { title : 'Sign-in Form', messages : req.flash('error') || req.flash('info') }); }else { return res.redirect('/'); } }; exports.renderSignup = function(req,res,next) { if (!req.user) { res.render('signup', { title : 'Sign-up Form', messages : req.flash('error') }); } else { return res.redirect('/'); } }; exports.signup = function(req,res,next) { if (!req.user) { var user = new User(req.body); var message = null; user.provider = 'local'; user.save(function(err) { console.log('save'); if(err) { message = getErrorMessage(err); req.flash('error', message); return res.redirect('/signup'); } req.login(user, function(err) { if (err) return next(err); return res.redirect('/'); }); }); } else { return res.redirect('/'); } }; exports.signout = function(req,res) { req.logout(); res.redirect('/'); };
기존에 작성했던 코드를 싹 지우고 새롭게 재구성 하였다.
코드를 살펴보면 먼저 getErrorMessage() 메소드는 몽구스 error 객체에서 통합된 오류 메시지를 반환하는 비공개
메소드다. 만약 로그인과 회원가입에서 오류가 생긴다면 err 객체를 getErrorMessage 로 넘기게 되고
getErrorMessage 메소드는 err 객체를 받아서 메시지를 반환한다. err.code 는 MongoDB 자체에서 에러가 나서
생성되는 MongoDB 의 에러코드 를 받는 것 이고 , err.errors 는 우리가 만든 몽구스의 검증 오류 메시지이다.
renderSignup 과 renderSignin 은 위에서 만든 화면을 그리기 위해 호출되며 title 과 메시지를 포함하고 해당화면
으로 가게 될 것 이다.
signout 같은 경우 인증된 세션을 무효화 하기 위해 패스포트 모듈이 제공하는 req.logout()
메소드를 사용하는 것 이 끝이다.
signup 은 새로운 사용자를 생성하기 위해 User 모델을 사용한다. 먼저 HTTP 요청 내용에서 user 객체를 생성하고
mongoDB 에 저장을 실시하는데 , 이 과정에서 오류가 발생하면 getErrorMessage 메소드를 통해 에러 메시지를
사용자에게 제공하게 되고, 사옹자 생성에 성공하게 된다면 패스포트 모듈이 제공하는 req.login 모듈을 사용하여
사용자 세션을 생성하게 된다.
여기서 req.flash 라는 새로운 것이 눈에 보일 것 이다. 이 모듈은 flash 라는 세션 객체 영역에 임시 메시지를
저장하게 만드는 노드 모듈이다. flash 객체에 저장된 메시지는 일단 사용자에게 보여지고 나면 지워질 것 이다.
package.json 파일을 사용하든 cmd 창을 사용하든 connect-flash 모듈을 설치하자.
설치가 다 되었다면 이놈을 사용하기 위해 express 에서 이 모듈을 부를 필요가 있다. config 폴더로 가서
express_config.js 파일을 수정하자.
var express = require('express'), morgan = require('morgan'), compress = require('compression'), bodyParser = require('body-parser'), methodOverride = require('method-override'), config = require('./config'), session = require('express-session'), passport = require('passport'), flash = require('connect-flash') // 추가 ; module.exports = function() { var app = express(); if(process.env.NODE_ENV === 'development') { app.use(morgan('dev')); } else if (process.env.NODE_ENV === 'production') { app.use(compress()); } app.use(bodyParser.urlencoded({ extended : true })); app.use(bodyParser.json()); app.use(methodOverride()); app.use(session({ saveUninitialized : true, resave : true, secret : config.sessionSecret })); app.set('views','./app/views'); app.set('view engine', 'ejs'); app.use(flash()); // 추가 app.use(passport.initialize()); app.use(passport.session()); require('../app/routes/index.server.routes.js')(app); require('../app/routes/users.server.routes.js')(app); app.use(express.static('./static')); return app; }
flash 모듈을 require 로 올리고 app.use 로 사용하게 만들었다.
이렇게 하면 사용자 세션 영역에 flash 라는 새로운 영역을 생성한다.
위 users.server.controller 파일을 살펴보면 renderSignup 과 renderSignin 부분에 req.flash 로 flash 영역에 메시지
를 저장하는 부분이 있다.
마지막으로 사용자의 라우트를 연결해보자. app/routes/ 폴더로 가서 users.server.routes.js 파일을 수정 하자.
var users = require('../../app/controllers/users.server.controller'), passport = require('passport'); module.exports = function(app) { app.route('/signup') .post(users.signup) .get(users.renderSignup); app.route('/signin') .get(users.renderSignin) .post(passport.authenticate('local', { successRedirect : '/', failureRedirect : '/signin', failureFlash : true })); app.get('/signout', users.signout); };
라우팅 파일 역시 새롭게 정의하였다.
다른 부분은 간단히 이해가 되지만 , /signin 경로의 post 요청은 살펴봐야 하겠다.
여기로 들어온다면 passport.authenticate 메소드가 수행되며 , 첫번째 인수로 받은 인증전략을 사용하는데
여기서 우리는 미리 만들어 놓은 local 전략을 사용할 것 이다.
그리고 밑에 옵션으로 들어있는 부분은
successRedirect : 이 속성은 패스포트에게 성공적으로 사용자를 인증한 다음에 요청을 전환할 위치를 알려준다.
failureRedirect : 이 속성은 패스포트에게 사용자가 인증에 실해한 다음에 요청을 전환 할 위치를 알려준다.
failureFlash : 이 속성은 패스포트에게 flash 메시지를 사용할 지 말지를 알려준다.
자 이제 기본적인 인증절차는 거의 완성되었다.
이제 로그인한 유저가 보는 index 화면을 만들자. 먼저 사용자의 이름이 나올 수 있도록 기본 경로에 사용자의
이름 변수를 추가하자. app/controllers/ 폴더로 가서 index.server.controller.js 파일을 변경하자.
exports.render = function(req,res) { res.render('index', { title : 'Hello World', username : req.user ? req.user.username : '' }); };
인덱스 라우팅 파일 역시 새롭게 정의하였다. index.ejs 에서 보여 줄 title 변수와 username 변수를 정의하였다.
정말 마지막으로 app/views 폴더로 가서 index.ejs 파일을 변경하자.
<!DOCTYPE HTML> <html> <head> <title> <%= title %> </title> </head> <body> <% if (username) { %> <h2>Hello <%=username %></h2> <a href="/signout">Sign out</a> <% } else { %> <a href="/signup">Signup</a> <a href="/signin">Signin</a> <% } %> <br /> <img src="images/overwatchlogo.png" alt="logo" /> </body> </html>
자 이제 모든 것이 완성 되었으니 테스트를 해보자.
mongodb 켜고 StartApp.js 를 실행하여 서버를 가동 시키자.
먼저 http://localhost:3000 으로 접속을 해보자.
로그인이 아직 안되었기에 Sign up 링크와 sign in 링크만 보인다.
먼저 회원가입을 해보기 위해 Sign up 링크를 클릭해보자.
만들어 둔 회원가입 폼이 생겼다.
각각 항목을 입력하고 Sign up 버튼을 클릭하자.
접속이 잘 되는가? 방금 등록한 정보로 로그인이 되었다.
이제 Sign out 링크를 클릭하여 로그아웃 한 뒤 Sign in 으로 로그인도 해보자.
로그인역시 잘 동작해야 한다.
그리고 db에 어떤 방식으로 저장되었는지 확인을 해보자.
salt 값이 저장되어있고 , provider 필드에는 local 전략이 들어가있고 password 는 암호화되어 저장 되었다.
이로써 passport 를 활용한 회원가입과 로그인을 완성하였다.
아직 디자인도 다듬지 않았고 back 버튼이라던지 다른 추가 기능이 들어가지 않았지만 , 웹 서비스의 중요한
부분인 인증부분을 완성한 것에 만족하고 , 본격적인 서비스는 angular.js 와 함께 만들 것 이다.
다음 장에는 OAuth 전략으로 외부 공급자를 통한 로그인을 구현 할려 했으나 (구글 , 페이스북, 트위터 연동)
이 부분은 패스하고 바로 angular.js 로 넘어가도록 하겠다.
혹시나 작동이 되지 않는다면 14장 과 15장을 참고해서 오타는 없는지 빠진 것은 없는지 꼼꼼히 확인하길
바란다. (현재 14장의 코드가 조금 수정되었다. 꼭 다시 확인하자.)
(끝)
(현재까지 파일과 폴더 구조)
'MeanStack (deprecated)' 카테고리의 다른 글
17장 Angular.js 를 이용한 MVC 엔티티 (2) 2016.07.05 16장 Angular.js 설치 , bower 를 이용한 의존성 관리 (1) 2016.06.30 14장 passport 모듈을 이용한 사용자 인증 (1) (8) 2016.06.23 13장 mongoose(몽구스)를 이용한 index 활용, 데이터 검증 (5) 2016.06.21 12장 mongoose(몽구스) 를 이용한 스키마 확장 (1) 2016.06.19