노드에서 사용할 수 있는 사용자 인증 모듈
① 로컬인증 : 데이터베이스에 저장된 사용자 정보와 비교
② OAuth인증 : 페이스북, 구글, 네이버, 카카오 계정으로 로그인 - key값을 받아야 하니깐 eclipse에서 쓰지말고 쓰는법은 각 홈페이지 API 참조하는게 나음
사용자 인증 처리를 위한 필수 모듈
npm install passport --save
npm install passport-local --save - 로컬 인증 기능 (데이터베이스에 저장된 사용자 정보와 비교)
npm install connect-flash --save - 요청객체에 메세지를 넣어둘 수 있는 기능, 다른 함수나 뷰 템플릿 처리 함수에 메세지를 전달, 사용자에게 메세지 전달
* node_modules에 passport-strategy 모듈도 함께 설치되는데 이것은 OAuth 인증 (카카오,네이버,...) 기능이다
<사용자 로그인과 회원가입 처리 과정>
클라이언트 요청 | 웹 서버 | 뷰 템플릿 |
홈화면 조회 | / | 홈화면 index.ejs |
회원가입 조회 | /signup (get) | 회원가입화면 signup.ejs |
회원가입 요청 | /signup (post) | 회원가입처리 (함수로 처리) |
로그인화면 조회 | /login(get) | 로그인화면 login.ejs |
로그인 요청 | /login(post) | 로그인처리 (함수로 처리) |
사용자 프로필 | /profile(get) | 프로필화면 profile.ejs |
로그아웃 요청 | /logout(get) | 로그아웃처리 (함수로 처리) |
개발환경 - os: Window8.1 64x / 프로세서: Intel(R) Core(TM) i3-4020Y CPU @ 1.50GHz / RAM: 8.0GB
개발도구 - IDE: eclipse 4.4.2 (32bit) / DBMS: Oracle11g / 컴파일러: jdk 1.8.0 (java version)
개발언어 - Java, HTML5, CSS3, JavaScript(Node.js), SQL, jQuery, BootStrap
앞에서 연습했던 ModuleExe를 복사해와서 PassportExe 패키지를 생성한다(package.json 가서 "name" 바꿔줄것)
ModuleExe 패키지와의 차이점은 view단을 전부 ejs로 만들 것
차이점①
view단을 ejs로만 만들거기 때문에 router/user.js가 필요 없게됨
-> 이 말은 config.js 에 있던 라우터 정보도 필요 없게 된 것
-> url별 view단 매핑정보를 app2.js 에 기재해야 함
이전 config.js
//라우터 정보
route_info:[
{file:"./user", path:"/process/login",
method:"login", type:"post"},
{file:"./user", path:"/process/addUser",
method:"addUser", type:"post"},
{file:"./user", path:"/process/listUser",
method:"listUser", type:"post"}
]
변화 후 config.js
//라우터 정보
route_info:[
]
차이점②
인증방식을 email로 이용하기 위해 database/user_schema.js에서 id에 접근하던걸 email로 고쳐준다
Schema.createSchema = function(mongoose) {
UserSchema = mongoose.Schema({
email: {type:String, "default":""},
Schema가 바뀌면 테이블도 바뀌어야 하므로 config.js가서 db_schemas부분도 수정 (collection 부분)
//db참조
db_schemas:[
{file:"./user_schema",collection:"users4",
schemaName:"UserSchema",modelName:"UserModel"}
]
디자인 프레임워크 가져오기
1. 아이콘 무료 사이트 - https://fontawesome.com/
쓰고싶은 아이콘 클릭, Start Using This Icon 클릭
이것만 copy해 오면 됨
(주의) i쪽 자체가 icon이니까 그 안에다가 카드입니다 이런 문구 쓰는거 아님!
<i class="fas fa-address-card"></i>카드입니다 (O)
2. 부트스트랩
https://getbootstrap.com/
CDN 선언 (버전은 상이할 수 있음)
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css">
passport 설정
// passport 모듈
var passport = require("passport"); // 인증을 위한 필수 모듈
var flash = require("connect-flash"); // 사용자한테 메세지를 전달하는 모듈
//Passport 사용 설정 (반드시 세션설정 밑에 기술)
//Passport 의 두개의 함수를 호출했을 때 반환하는 객체를 미들웨어로 사용
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
**원래 router/user.js 에 있던 로그인, 회원가입 기능 등등을 app2.js에다가 써주려고 한다**
로그인 관련 라우팅 함수 설정
post 방식으로 요청할 때는 passport 객체의 authenticate() 메소드를 호출하여 패스포트 모듈에서 사용자 인증을 처리하도록 넘겨줌
//router4. 로그인화면조회
router.route("/login").get(function(req,res){
res.render("login.ejs",
{message:req.flash("loginMessage")});
});
//router5. 로그인요청 (결과페이지)
// 사용자 인증 - POST로 요청받으면 패스포트를 이용해 인증함
// 인증 실패 시 검증 콜백에서 설정한 flash 메시지가 응답 페이지에 전달되도록 함
router.route("/login")
.post(passport.authenticate("local-login",{
successRedirect : "/profile",
failureRedirect : "/login",
failureFlash : true
}));
login.ejs
<!-- flash 메세지 "등록된 계정이 없습니다" - 이 메세지 띄워줄것임 -->
<%if(message.length>0) { %>
<div class="alert alert-danger"><%=message %></div>
<%} %>
<form action="/login" method="post">
<div class="form-group">
<label>이메일</label>
<input type="text" class="form-control" name="email"/>
</div>
<div class="form-group">
<label>비밀번호</label>
<input type="password" class="form-control" name="pwd"/>
</div>
<button type="submit" class="btn btn-warning btn-lg">로그인</button>
</form>
메소드1. Passport 로그인 설정 (app2.js)
usernameField, passwordField의 value는 signup.ejs의 name 값과 동일해야 한다
이 값으로 데이터베이스의 값과 비교해서 인증 절차를 진행하게 되는데,
인증이 성공한 경우는 done(null, 유저 정보 객체)를 넘기게 된다
*
use(이름,인증방식객체) : 이름은 함수 구별 용도, 사용자 정의
done() : 예약어
usernameField, passwordField 어떤 폼 필드로부터 아이디와 비밀번호를 전달받을 지 설정하는 옵션
passReqToCallback : 인증을 수행하는 인증 함수로 HTTP request를 그대로 전달할지 여부를 결정
// Passport Strategy 설정 (어떤 인증방식 사용할건지 객체 생성)
var LocalStrategy = require("passport-local").Strategy;
// 'passport-local'모듈의 Strategy라는 메소드 가져온것
passport.use("local-login",
new LocalStrategy({usernameField : "email",passwordField : "pwd",
passReqToCallback : true}, function(req,email,pwd,done) {
// 콜백함수의 첫번째 파라미터로 req 객체가 전달됨
console.log("passport의 local-login 호출");
console.log(email + ":" + pwd);
// 데이터베이스 객체 생성
var database = app.get("database");
database.UserModel.findOne({"email":email}, function(err,user){
//조회하다가 에러 발생한 경우
if(err) {return done(err);} // 원래는 throw err; 이렇게 처리했음
// 1. 조회는 했는데 해당 이메일로 등록된 사용자가 없는 경우
if(!user) {
console.log("이메일이 일치하지 않음");
//검증 콜백에서 두번째 파라미터를 false로 해서 인증 실패한것으로 처리
return done(null,false,
req.flash("loginMessage","등록된 계정이 없습니다"));
// flash : 메세지 넘겨주는 애
}
// 2. 조회 했고 해당 이메일로 등록된 사용자를 찾은 경우
// 2-1. 비밀번호 비교
var authenticated =
user.authenticate(pwd,
user._doc.salt,user._doc.hashed_pwd);
if(!authenticated) {
console.log("비밀번호가 일치하지 않음");
return done(null,false,
req.flash("loginMessage","비밀번호가 일치하지 않음"));
}
// 2-2. 비밀번호까지 전부 일치한 경우
console.log("계정과 비밀번호가 일치함");
return done(null,user);
});
}
));
passport 객체의 serializeUser()와 deserializeUser() 메소드
- serializeUser() : 메소드를 호출하면서 등록한 콜백 함수는 사용자 인증이 성공적으로 진행되었을 때 호출됨
- deserializeUser() : 사용자 인증 이후 사용자 요청이 들어올 때마다 호출
//사용자 인증 시 호출
//사용자 정보를 이용해서 세션을 만듬
passport.serializeUser (function(user,done){
console.log("serializeUser 호출");
//여기 인증 콜백에서 넘겨주는 user 객체의 정보를 이용해서 세션을 생성
done(null,user); // 에러 없으니깐 null, 그리고 user넘겨주면 됨
});
//사용자 인증 이후 사용자가 요청할 때마다 호출하는 부분임
// 사용자 정보를 이용해서 세션을 만듬
passport.deserializeUser (function(user,done){
// 매개변수 user -> 사용자 인증 성공 시 serializeUser 메소드를 이용해 만들었던 세션 정보가
// 파라미터로 넘어온 것임 (serizlizeUser의 done의 인자 user를 받은 것)
//사용자 정보 중에서 id나 email만 있는 경우 사용자 정보 조회가 필요
// 여기에서는 파라미터로 받은 user를 별도로 처리하지 않고 그대로 넘겨줌
//두번째 파라미터로 지정한 사용자 정보(user)가 req.user가 됨
done(null,user);
});
signup.ejs
<%if(message.length>0) { %>
<div class="alert alert-danger"><%=message %></div>
<%} %>
<form action="/signup" method="post">
<div class="form-group">
<label>이메일</label>
<input type="text" class="form-control" name="email"/>
</div>
<div class="form-group">
<label>비밀번호</label>
<input type="password" class="form-control" name="pwd"/>
</div>
<div class="form-group">
<label>이름</label>
<input type="text" class="form-control" name="name"/>
</div>
<button type="submit" class="btn btn-warning btn-lg">회원가입</button>
</form>
회원가입 관련 라우팅 함수 설정
//router2. 회원가입조회
router.route("/signup").get(function(req,res){
res.render("signup.ejs",
{message:req.flash("signupMessage")});
});
//router3. 회원가입요청 (결과페이지)
router.route("/signup")
.post(passport.authenticate("local-signup",{
successRedirect : "/profile",
failureRedirect : "/signup",
failureFlash : true
// 패스포트로 인증하는 과정에서 오류 발생시 플래시 메시지가 오류로 전달된다.
}));
메소드2. Passport 회원가입 설정
passport.use("local-signup", new LocalStrategy({
usernameField : "email",
passwordField : "pwd",
passReqToCallback : true
}, function(req,email,pwd,done) {
// 사용자가 입력한 name, pwd를 받는다
var paramName = req.body.name || req.query.name;
console.log("passport의 local-signup 호출");
console.log(email + ":" + pwd);
process.nextTick(function(){
// 데이터베이스 객체 생성
var database = app.get("database");
database.UserModel
.findOne({"email":email}, function(err,user){
//조회하다가 에러 발생한 경우
if(err) {return done(err);}
// 1. 이미 등록된 사용자가 있는경우
if(user) {
console.log("이미 이메일 계정이 있습니다");
return done(null,false,
req.flash("signupMessage","이미 이메일 계정이 있습니다"));
} else {
// 2. 등록된 사용자가 없는 경우
console.log("등록된 사용자가 없으므로 회원가입 진행");
var user =
new database.UserModel({"email":email,
"pwd":pwd,
"name":paramName});
user.save(function(err){
if(err) {throw err;}
console.log("사용자 데이터 추가 완료");
return done(null,user); // user만 넘김
});
} // end...if
}); // findOne 끝
}); // nextTick 끝
})); // function 끝
profile.ejs
<div class="well">
<h3><span class="fa fa-user"></span>
로컬 프로필 정보</h3>
<br/>
<p>
<strong>이메일</strong> : <%=user.email %> <br/><br/>
<strong>이름</strong> : <%=user.name %> </p>
<br/>
</div>
사용자 프로필 관련 라우팅 함수 설정
//router6. 사용자프로필
router.route("/profile").get(function(req,res){
// (1) 인증이 안된 경우 (= 로그인 안된 경우)
if(!req.user) {
console.log("사용자 인증이 안된 상태임");
res.redirect("/"); // 홈화면으로
return;
}
// (2) 인증이 된 경우 (= 로그인 된 경우)
console.log("사용자 인증이 된 상태임");
// js에서는 배열 타입을 별도로 제공하지 않는다
// 배열은 객체 타입 (object) 로 처리되며 배열인지 아닌지 확인할 수 있는 방법이
// Array.isArray 이다
if(Array.isArray(req.user)) {
res.render("profile.ejs",{user: req.user[0]._doc});
} else {
res.render("profile.ejs",{user: req.user});
}
});
로그아웃 관련 라우팅 함수 설정
//router7. 로그아웃 요청
router.route("/logout").get(function(req,res){
req.logout();
res.redirect("/");
});
결과화면
'Node' 카테고리의 다른 글
Node.js - Socket.IO 모듈을 이용한 채팅 프로그램 (0) | 2019.11.12 |
---|---|
Passport 모듈로 회원가입 및 로그인 하기 - Passport 모듈화 적용 (0) | 2019.11.11 |
Node.js 에 View Template 적용하기 (1) - Semantic UI (미완) (0) | 2019.11.08 |
Node.js + Oracle 을 이용한 로그인/패스워드 웹서버 구축하기 (0) | 2019.11.07 |
Node.js + Oracle (0) | 2019.11.06 |