Last active
October 23, 2019 03:47
-
-
Save pjchender/bba7bb7bc819e6997d8a17b2a014c68f to your computer and use it in GitHub Desktop.
Learn to Use Passport(Passport 學習筆記)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 匯入需要的模組 | |
const express = require('express') | |
const bodyParser = require('body-parser') | |
const cookieParser = require('cookie-parser') | |
const path = require('path') | |
const logger = require('morgan') | |
const mongoose = require('mongoose') | |
const session = require('express-session') | |
const passport = require('./middleware/passport') | |
const MongoStore = require('connect-mongo')(session) // 直接執行並將 session 存進去,logout 後會自動刪除該 document | |
const dbconfig = require('./db') | |
// 和 mongoDB 連線 | |
mongoose.connect(dbconfig.connection) // 等同於,mongoose.connect('mongodb://localhost:27017/bookworm') | |
const db = mongoose.connection | |
db.on('error', console.error.bind(console, 'connection error')) // mongo error handler | |
// 載入 express | |
const app = express() | |
// 設定 view engine 和模版路徑 | |
app.set('view engine', 'pug') | |
app.set('views', path.join('./views')) | |
// middleware | |
app.use(logger('dev')) | |
app.use(bodyParser.json()) | |
app.use(bodyParser.urlencoded({ extended: true })) | |
app.use(cookieParser()) | |
app.use(express.static(path.join('./public'))) // 讀取 ./public 中的靜態檔案 | |
// 使用 session 來追蹤使用者 | |
app.use(session({ | |
secret: 'I love NodeJS', // secret: 必要欄位,用來註冊 session ID cookie 的字串。如此將增加安全性,避免他人在瀏覽器中偽造 cookie。 | |
resave: false, // resave: 不論是否 request 的過程中有無變更都重新將 session 儲存在 session store。 | |
saveUninitialized: false, // saveUninitialized: 將 uninitialized session(新的、未被變更過的) 儲存在 session store 中。 | |
store: new MongoStore({ | |
mongooseConnection: db | |
}) | |
})) | |
app.use(passport.initialize()) | |
app.use(passport.session()) | |
// 讓 userID 可以在 template 中被存取,名稱為 currentUser | |
app.use(function (req, res, next) { | |
res.locals.currentUser = req.session.userId // res.locals 屬性在所有 view 中都可以存取到 | |
next() // 執行下一個 middleware | |
}) | |
// 載入路由檔 | |
const index = require('./routes/index') | |
app.use('/', index) | |
// catch 404 and forward to error handler | |
app.use(function (req, res, next) { | |
var err = new Error('File Not Found') | |
err.status = 404 | |
next(err) | |
}) | |
// error handler | |
// define as the last app.use callback | |
app.use(function (err, req, res, next) { | |
res.status(err.status || 500) | |
res.render('error', { | |
message: err.message, | |
error: {} | |
}) | |
}) | |
// listen on port 3000 | |
app.listen(3000, function () { | |
console.log('Express app listening on port 3000') | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./routes/index | |
const express = require('express') | |
const router = express.Router() | |
const User = require('../models/user') | |
const mid = require('../middleware') // 將自己寫的 middleware 載入 | |
const passport = require('../middleware/passport') | |
// POST /login | |
router.post('/login', function (req, res, next) { | |
passport.authenticate('login', function (err, user, info){ | |
if (err) return next(err) | |
if (!user) { | |
err = new Error('User not found') | |
res.status(409) | |
return next(err) | |
} | |
req.login(user, function (err) { | |
if (err) return next(err) | |
req.session.userId = user._id | |
return res.redirect('/profile') | |
}) | |
})(req, res, next) | |
}) | |
// POST /register | |
router.post('/register', function (req, res, next) { | |
passport.authenticate('signup', function (err, user, info) { | |
if (err) { | |
err = new Error('Singup Error') | |
err.status = 409 | |
return next(err) | |
} | |
if (!user) { | |
err = new Error(info) | |
err.status = 409 | |
return next(err) | |
} | |
req.login(user, function (err) { | |
if (err) return next(err) | |
console.log('User account created successfully') | |
req.session.userId = user._id | |
return res.redirect('/profile') | |
}) | |
})(req, res, next) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const passport = require('passport') | |
const bcrypt = require('bcrypt') // hashing module | |
const LocalStrategy = require('passport-local') | |
const JwtStrategy = require('passport-jwt').Strategy | |
const ExtractJwt = require('passport-jwt').ExtractJwt | |
const User = require('../models/user') | |
const jwtConfig = require('../config/jwt') | |
/** | |
* passport.use('驗證策略名稱', '想建立的策略類型') | |
* passReqToCallback: 讓我們在後面的 callback 中可以使用 req 參數 | |
*/ | |
// Passport Initialization | |
passport.serializeUser(function (user, done) { | |
done(null, user._id) | |
}) | |
passport.deserializeUser(function (id, done) { | |
User.findById(id, function (err, user) { | |
done(err, user) | |
}) | |
}) | |
let jwtStrategy = new JwtStrategy({ | |
secreteOrKey: jwtConfig.secret, | |
jwtFromRequest: ExtractJwt.fromExtractors([ | |
ExtractJwt.versionOneCompatibility({authScheme: 'Bearer'}), | |
ExtractJwt.fromAuthHeader() | |
]) | |
}, function (payload, done) { | |
User.findById(payload.sub, function (err, user) { | |
if (err) return done(err) | |
if (!user) return done(null, false, {message: 'Wrong JWT Token'}) | |
if (payload.aud !== user.email) return done(null, false, {message: 'Wrong JWT Token'}) | |
const exp = payload.exp | |
const nbf = payload.nbf | |
const curr = ~~(new Date().getTime() / 1000) | |
if (curr > exp || curr < nbf) { | |
return done(null, false, 'Token Expired') | |
} | |
return done(null, user) | |
}) | |
}) | |
let loginStrategy = new LocalStrategy({ | |
usernameField: 'email', | |
passReqToCallback: true | |
}, function (req, email, password, done) { | |
User.findOne({ email: email }, function (err, user) { | |
if (err) { | |
return done(err) | |
} | |
if (!user) { | |
return done(null, false, 'Username is not exists') | |
} | |
let isValidPassword = function (user, password) { | |
return bcrypt.compareSync(password, user.password) | |
} | |
if (!isValidPassword(user, password)) { | |
return done(null, false, 'Invalid Password') | |
} | |
return done(null, user) | |
}) | |
}) | |
let signupStrategy = new LocalStrategy({ | |
usernameField: 'email', | |
passReqToCallback: true | |
}, function (req, email, password, done) { | |
if (password !== req.body.confirmPassword) { | |
return done(new Error('Confirmation is not match with password')) | |
} | |
// if (password.length < 8) { | |
// return done(new Error('Password must has 8 characters at least')) | |
// } | |
const findOrCreateUser = function () { | |
User.findOne({ email: email }, function (err, user) { | |
if (err) { | |
return done(err) | |
} | |
if (user) { | |
return done(null, false, 'Username Already Exists') | |
} else { | |
let newUser = new User() | |
newUser.email = email | |
newUser.password = bcrypt.hashSync(password, 10) | |
newUser.name = req.body.name | |
newUser.favoriteBook = req.body.favoriteBook | |
newUser.save(function (err, user) { | |
if (err) { | |
throw err | |
} | |
return done(null, user) | |
}) | |
} | |
}) | |
} | |
process.nextTick(findOrCreateUser) | |
}) | |
passport.use('jwt', jwtStrategy) | |
passport.use('login', loginStrategy) | |
passport.use('signup', signupStrategy) | |
module.exports = passport |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./models/user | |
const mongoose = require('mongoose') // ODM for Mongo | |
const UserSchema = new mongoose.Schema({ | |
email: { | |
type: String, | |
required: true, | |
trim: true, | |
unique: true | |
}, | |
name: { | |
type: String, | |
required: true, | |
trim: true | |
}, | |
favoriteBook: { | |
type: String, | |
required: true, | |
trim: true | |
}, | |
password: { | |
type: String, | |
required: true | |
} | |
}) | |
var User = mongoose.model('User', UserSchema) | |
module.exports = User |
推薦閱讀
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
[app.js]
[middleware]
app.use(passport.initialize())
app.use(passport.session())
,記得要放在express.session()
的後面