생활 코딩 강의 목록 : https://opentutorials.org/module/3590
인프런 강의 목록 : https://www.inflearn.com/course/node-js-express/dashboard
이 수업은 CC 라이센스를 따르고 있으며, 아래 링크 에서도 볼 수 있습니다.
Node.js-Express https://opentutorials.org/course/3370
쿠키와 인증 https://opentutorials.org/course/3387
세션과 인증 https://opentutorials.org/course/3400 passpor.js
https://opentutorials.org/course/3402
다중 사용자 https://opentutorials.org/course/3411 google login https://opentutorials.org/course/3413 facebook login
https://opentutorials.org/course/3414
소스 : https://github.com/braverokmc79/Nodejs-master-passpot
Passport.js
53. Express Session & Auth 수업소개
const FileStore = require('session-file-store')(session); /** 세션 파일 저장 방식 FileStore app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true, store: new FileStore() })); */ // session DB 저장 방식 - 테이블이 자동 생성되고 세션이 저장 된다
54. passport.js 설치
55. 인증 구현
56. 자격확인1
57. 자격확인2
58. 세션이용1
59. 세션이용2
60. 세션이용3
61.로그인 확인
62.로그아웃
63.플래쉬 메시지
https://github.com/jaredhanson/connect-flash
app.get('/flash', function (req, res) { req.flash('msg', 'Flash is back!!!'); res.send("fmsg"); }); app.get('/flash-display', function (req, res) { const fmsg = req.flash(); console.log(fmsg); res.send(fmsg); });
64.플래쉬 메시지의 적용
65.리팩토링
66.수업을 마치며
1) 공식 문서 참조
1) https://www.passportjs.org/
2)https://www.npmjs.com/package/passport
3) https://www.passportjs.org/howtos/password/
4)https://www.passportjs.org/tutorials/password/verify/
5) https://www.passportjs.org/concepts/authentication/downloads/html/
2) 설치
1. passport 설치
$ npm install passport
2. passport-local 설치
$ npm install passport-local
3. mysql 설치
$npm i mysql
4. express-mysql-session 설치
& npm i express-mysql-session
5. flash 설치
$ npm install connect-flash
3) mysql 접속 테스트
nodejs/mysql.js
const mysql = require('mysql'); // 비밀번호는 별도의 파일로 분리해서 버전관리에 포함시키지 않아야 합니다. const connection = mysql.createConnection({ host: 'localhost', user: 'opentutorials', password: '1111', database: 'opentutorials' }); connection.connect(); connection.query('SELECT * FROM topic', function (error, results, fields) { if (error) { console.log(error); } console.log('The solution is :', results); }); connection.end();
$nodemon nodejs/mysql.js
4) 설정 주의
app.use session 설정 다음에 반드시 passport 추가할것
const express = require('express') const fs = require('fs'); const bodyParser = require('body-parser') const compression = require('compression') const session = require('express-session') const MySQLStore = require('express-mysql-session')(session); const flash = require('connect-flash'); const app = express() const port = 3000 const helmet = require('helmet') app.use(helmet()) app.use(express.static('public')) app.use(bodyParser.urlencoded({ extended: false })); app.use(compression()); // session DB 저장 방식 - session 테이블이 자동 생성되고 세션이 passport의해 저장 된다. app.use(session({ secret: '12312dajfj23rj2po4$#%@#', resave: false, saveUninitialized: true, store: new MySQLStore({ host: 'localhost', port: 3306, user: 'opentutorials', password: '1111', database: 'opentutorials' }) })); app.use(flash()); const passport = require("./lib/passport")(app); app.get('*', (req, res, next) => { fs.readdir('./data', function (error, filelist) { req.list = filelist; next(); }); }); ~
5) passport 회원가입 및 로그인 구현
중요하게 볼 파일은
main.js 파일에 passport 설정 부분,
lib/passport.js
routes/auth.js
파일이다.
const express = require('express') const session = require('express-session') const MySQLStore = require('express-mysql-session')(session); const flash = require('connect-flash'); const app = express() const port = 3000 ~ // session DB 저장 방식 - session 테이블이 자동 생성되고 세션이 passport의해 저장 된다. app.use(session({ secret: '12312dajfj23rj2po4$#%@#', resave: false, saveUninitialized: true, store: new MySQLStore({ host: 'localhost', port: 3306, user: 'opentutorials', password: '1111', database: 'opentutorials' }) })); app.use(flash()); const passport = require("./lib/passport")(app); const authRouter = require("./routes/auth")(passport); app.use('/auth', authRouter); ~
1. main.js
const express = require('express') const fs = require('fs'); const bodyParser = require('body-parser') const compression = require('compression') const session = require('express-session') const MySQLStore = require('express-mysql-session')(session); const flash = require('connect-flash'); const app = express() const port = 3000 const helmet = require('helmet') app.use(helmet()) app.use(express.static('public')) app.use(bodyParser.urlencoded({ extended: false })); app.use(compression()); // session DB 저장 방식 - session 테이블이 자동 생성되고 세션이 passport의해 저장 된다. app.use(session({ secret: '12312dajfj23rj2po4$#%@#', resave: false, saveUninitialized: true, store: new MySQLStore({ host: 'localhost', port: 3306, user: 'opentutorials', password: '1111', database: 'opentutorials' }) })); app.use(flash()); const passport = require("./lib/passport")(app); app.get('*', (req, res, next) => { fs.readdir('./data', function (error, filelist) { req.list = filelist; next(); }); }); const indexRouter = require("./routes/index"); const authRouter = require("./routes/auth")(passport); const topicRouter = require("./routes/topic"); app.use("/", indexRouter); app.use('/auth', authRouter); app.use('/topic', topicRouter); app.use(function (req, res) { res.status(400).send("Sorry cant find that!"); }); app.use((err, req, res, next) => { console.error(err.stack) res.status(500).send('Something broke!') }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
2. lib/db.js
const mysql = require('mysql'); const db = mysql.createConnection({ host: 'localhost', user: 'opentutorials', password: '1111', database: 'opentutorials' }); db.connect(); module.exports = db;
3. lib/auth.js
module.exports = { isOwner: function (req, res) { console.log(" isOwner req.user : ", req.user); if (req.user) return true; else return false; }, statusUI: function (req, res) { let authStatusUI = '<a href="/auth/login">login</a> <a href="/auth/signup">signup</a>'; if (this.isOwner(req, res)) { authStatusUI = `${req.user.username} <a href="/auth/logout">logout</a> `; } return authStatusUI; } }
★현재 프로젝트에서는 적용하지 않았으나,
passReqToCallback: true 옵션값을 넣어 req 파라미터값을 사용할 수 있다.
passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password', passReqToCallback: true }, function verify(req, email, password, done) { console.log('local-join callback called'); console.log(email, password, done); } ));
4. lib/passport.js
const passport = require("passport"); const crypto = require('crypto'); const db = require("../lib/db"); const LocalStrategy = require('passport-local'); module.exports = function (app) { app.use(passport.initialize()); app.use(passport.session()); passport.serializeUser(function (user, cb) { console.log("로그인 처리시 최초 한번 passport.serializeUser 호출 user 값 :", user); process.nextTick(function () { cb(null, { id: user.id, username: user.username }); }); }); // 로그인 성공되면 passport.deserializeUser 매번 실행 처리된다 passport.deserializeUser(function (user, cb) { console.log("deserializeUser :", user); process.nextTick(function () { return cb(null, user); }); }); // 로그인 처리 - 2) passport 로그인 passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' }, function verify(email, password, cb) { db.query('SELECT * FROM users WHERE email = ?', [email], function (err, row) { if (err) { return cb(err); } if (!row[0]) { return cb(null, false, { message: '등록된 이메일이 없습니다.' }); } crypto.pbkdf2(password, row[0].salt, 310000, 32, 'sha256', function (err, hashedPassword) { console.log("로그인 데이터 : ", password, row[0].salt, hashedPassword.toString('hex')); console.log("로그인 DB암호: ", row[0].hashed_password); if (err) { return cb(err); } if (row[0].hashed_password !== hashedPassword.toString('hex')) { console.log("비밀번호 오류"); //flash 에 저장 처리됨 - "flash":{"error":["비밀번호가 일치하지 않습니다."]}} return cb(null, false, { message: '비밀번호가 일치하지 않습니다.' }); } console.log("로그인 성공"); return cb(null, row[0], { message: '로그인 성공' }); }); }); })); return passport; }
5. routes/auth.js
const express = require('express'); const router = express.Router(); const template = require('../lib/template.js'); const auth = require("../lib/auth.js"); const db = require("../lib/db"); global.crypto = require('crypto') module.exports = function (passport) { //로그인 페이지 router.get("/login", (req, res) => { const fmsg = req.flash(); let feedback = ''; if (fmsg.error) { feedback = fmsg.error[0]; } const title = 'WEB - login'; const list = template.list(req.list); const html = template.HTML(title, list, ` <div style="color:red">${feedback}</div> <form action="/auth/login_process" method="post"> <p><input type="email" name="email" placeholder="email"></p> <p><input type="password" name="password" placeholder="password"></p> <p> <input type="submit" value="login"> </p> </form> `, '', auth.statusUI(req, res)); res.send(html); }); // 로그인 처리 - 1) 성공 및 실패 페이지 설정 및 flash 사용여부 설정하기 router.post('/login_process', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth/login', failureFlash: true, successFlash: true })); //로그 아웃 처리 router.get('/logout', function (req, res, next) { req.logout(function (err) { if (err) { return next(err); } req.session.destroy(function (err) { res.redirect("/"); }) }); }); //회원가입페이지 router.get('/signup', function (req, res, next) { const fmsg = req.flash(); let feedback = ''; console.log("회원 가입페이지 : ", fmsg); if (fmsg.error) { feedback = fmsg.error[0]; } const title = 'WEB - login'; const list = template.list(req.list); const html = template.HTML(title, list, ` <div style="color:red">${feedback}</div> <h1>Sign up</h1> <form action="/auth/signup_process" method="post"> <section> <label for="username">이름</label> <input id="username" name="username" type="text" placeholder="이름" required> </section> <section> <label for="email">이메일</label> <input id="email" name="email" type="email" placeholder="이메일" required> </section> <section> <label for="password">비밀번호</label> <input id="password" name="password" type="password" autocomplete="new-password" placeholder="비밀번호" required> </section> <section> <label for="password">비밀번호확인</label> <input id="password" name="pw2" type="password" autocomplete="new-password" placeholder="비밀번호확인" required> </section> <button type="submit">Sign up</button> </form> `, '', auth.statusUI(req, res)); res.send(html); }); //회원가입처리 router.post('/signup_process', function (req, res, next) { if (req.body.password !== req.body.pw2) { req.flash("error", "비밀번호가 일치 하지 않습니다."); return res.redirect("/auth/signup"); } crypto.randomBytes(16, (error, buf) => { const salt = buf.toString("base64"); crypto.pbkdf2(req.body.password.trim(), salt, 310000, 32, 'sha256', function (err, hashedPassword) { console.log("회원가입 데이터 : ", req.body.password, salt, hashedPassword.toString('hex')); if (err) { return next(err); } db.query('INSERT INTO users (username,email, hashed_password, salt) VALUES (?, ?, ?, ?)', [ req.body.username, req.body.email, hashedPassword.toString('hex'), salt ], function (err, results, fields) { if (err) { req.flash("error", err.toString()); return res.redirect('/auth/signup'); } var user = { id: this.lastID, username: req.body.username }; console.log("등록한 insertId :", results.insertId); req.login(user, function (err) { if (err) { return next(err); } req.flash("success", "회원가입을 축하합니다."); res.redirect('/'); }); }); }); }); }); return router; }
6.routes/index.js
const express = require('express'); const router = express.Router(); const template = require('../lib/template.js'); const auth = require("../lib/auth.js") router.get('/', (req, res) => { const fmsg = req.flash(); let feedback = ''; if (fmsg.success) { feedback = fmsg.success[0]; } let title = 'Welcome'; const description = 'Hello, Node.js'; const list = template.list(req.list); const html = template.HTML(title, list, ` <div style="color:blue">${feedback}</div> <h2>${title}</h2>${description} <img src="/images/hello.jpg" style="width:300px; display:block; margin-top:10px" > `, `<a href="/topic/create">create</a>`, auth.statusUI(req, res) ); res.send(html); }); module.exports = router;
댓글 ( 4)
댓글 남기기