1. 基于session的注册、登录、登出
session存储到数据库
新建user.js
,添加users
mongoose model
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var User = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
admin: {
type: Boolean,
default: false
}
})
module.exports = mongoose.model('User', User)
实现注册、登录、登出功能
新建users.js
文件
var express = require('express')
const bodyParser = require('body-parser')
var User = require('user.js')
var router = express.Router()
router.use(bodyParser.json())
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.post('/signup', (req, res, next) => {
User.findOne({username: req.body.username})
.then((user) => {
if (user != null) { // 注册用户已存在
var err = new Error('User ' + req.body.username + ' already exists!')
err.status = 403
next(err)
} else { // 没有该用户
return User.create({
username: req.body.username,
password: req.body.password
})
}
})
.then((user) => {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.json({status: 'Registration Successful!', user: user})
}, (err) => next(err))
.catch((err) => next(err))
})
router.post('/login', (req, res, next) => {
if(!req.session.user) {
var authHeader = req.headers.authorization
if (!authHeader) {
var err = new Error('You are not authenticated!')
res.setHeader('WWW-Authenticate', 'Basic')
err.status = 401
return next(err)
}
var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':')
var username = auth[0]
var password = auth[1]
User.findOne({username: username})
.then((user) => {
if (user === null) {
var err = new Error('User ' + username + ' does not exist!')
err.status = 403
return next(err)
} else if (user.password !== password) {
var err = new Error('Your password is incorrect!')
err.status = 403
return next(err)
} else if(user.username === username && user.password === password) {
req.session.user = 'authenticated'
req.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('You are authenticated!')
}
})
.catch((err) => next(err))
} else {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('You are authenticated!')
}
})
router.get('/logout', (req, res) => {
if (req.session) {
req.session.destroy() // 删除session
res.clearCookie('session-id') // 清除客户端的cookie
res.redirect('/') // 重定向到应用主页
} else {
var err = new Error('You are not logged in!')
err.status = 403
next(err)
}
})
更改app.js
...
app.use('/', indexRouter)
app.use('/users', usersRouter)
function basicAuth(req, res, next) {
if (!req.session.user) {
var err = new Error('You are not authenticated!')
err.status = 403
return next(err)
} else {
if (req.session.user === 'authenticated') {
next()
} else {
var err = new Error('You are not authenticated!')
err.status = 403
return next(err)
}
}
}
...
2. Passport优化冗余代码
正如上面的代码中有有许多重复的错误检查代码和重复的认证操作,可以使用Passport
中间价进行简化。Passport
支持不同的认证策略,如OpenID、OAuth、OAuth2.0等。在这里,使用Local Strategy
来简化用户名密码注册的认证方式。
更新user.js
,使用passport-local-mongoose
模块简化username
和password
存储
var passportLocalMongoose = require('passport-local-mongoose')
var User = new Schema({
admin: {
type: Boolean,
default: false
}
})
User.plugin(passportLocalMongoose)
passport-local-mongoose
plugin adds in the username and a encrypted way of storing the password within our user model. 将注册用户的密码进行哈希加密。使用salt
为password加密,salt
是一个用来执行密码hash
操作的随机字符串。经hash加密后的密码存储在数据库中,原始的真实密码并没有被存储。当用户使用用户名密码进行认证时,密码经hash
后,与数据库中存储的密码进行比对。
所以,Passport-Local-Mongoose
在user model
上添加了认证方法,自动进行认证;基于用户名和密码的Local Strategy
认证方法可以通过user.authenticate
直接使用。
以用户名test,密码password为例,存储到mongoDB中的内容如下:
{
"_id" : ObjectId("5e69d49249cd2322286205b6"),
"admin" : false,
"username" : "test",
"salt" : "706639aa4b1d0627629ca0372e3e945189c6d2c6f534dcead55518d13200127c",
"hash" : "e9141ec79f2a0bcab0af3b4bc337c76b635e36f5cc3727f3d71e24224e559dda87e3e3e5f309d4220c50e1bc0fc10d2d78d007004e0076e81e43a9b05c3393bce5ca7f4de65bef0c40c4e9383aade96263a5a56ca81c9992e859e7e9f5daab2811df49fa897b731d313769c2f3bb2a73f7dd8bbb3c14b3bcf5088c57cb1439db03238f1d542dc56008b067f0061c5f0d91f0ce89c053180630c2e086ebb6fb7f5a26a67a4e5cabe63887beec82af6e757ebf945d727564ee09494d05f3b5fb05e33e718c01e44e7cef10344dd6b2ee02da9717ac410c8259ae752acf8d97e931cc6be2d32981af6c0a24c51fcdbde44dc44bb220abe09407dfadd5c6d3d7a5699ea35db46a3acb0e6881cca77ba2802a75e97f9be8c719792dfc9847e3f0af1b97a0152f1d772eea4c30a0a18e9dc5621bdab77255e958f509402aeb9a2b8238d78e72c46f3b08cb76425aa5ebcdbe80ec7541c3b6b394529be98eed96be5622366710db7388c599d3412dea143b233da9e429ded07bbd1159e41b8ab96a73f79ab15e5734a961c13e276445a8c2f28d315b0e48784918422709763871c1074ccbfc1d9841f88719d529ba0ab90d2d71b4db039213ffb4e072da7787f022564b1ad285b0512e2679edc461286b5c51deb0c0ec9cfbc4a70cecb8e531471a4adeff27b69d0cb0aec4b4f155d3a14d9f03af7f62428b3a895620aa26b3f4a67f09",
"__v" : 0
}
// 这里可没有存密码
新建authenticate.js
文件
var passport = require('passport')
// passport-local支持用户名密码的local strategy认证
var LocalStrategy = require('passport-local').Strategy
var User = require('user')
exports.local = passport.use(new LocalStrategy(User.authenticate()))
passport.serializeUser(User.serializeUser())
passport.deserializeUser(User.deserializeUser())
Passport
中间件也支持sessions
,但有用户信息要序列化后与session
信息存储在服务端,收到请求后,用户信息需要反序列化从session
信息中提取出。Passport-Local-Mongoose
插件通过serializeUser
和deserializeUser
支持序列化和反序列化操作。
更改users.js
文件
...
var passport = require('passport')
...
router.post('/signup', (req, res, next) => {
User.register(new User({username: req.body.username}), req.body.password, (err, user) => {
if (err) {
res.statusCode = 500
res.setHeader('Content-Type', 'application/json')
res.json({err: err})
} else {
passport.authenticate('local')(req, res, () => {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.json({success: true, status: 'Registration Successful!'})
})
}
})
})
router.post('/login', passport.authenticate('local'), (req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.json({success: true, status: 'You are successfully logged in!'})
})
更改app.js
...
var passport = require('passport')
var authenticate = require('./authenticate')
...
app.use(passport.initialize())
app.use(passport.session())
...
function basicAuth(req, res, next) {
console.log(req.user)
if(!req.user) {
var err = new Error('You are not authenticated!')
err.status = 403
next(err)
} else {
next()
}
}
...