session存储到MongoDB

1. 基于session的注册、登录、登出

session存储到数据库

新建user.js,添加usersmongoose 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模块简化usernamepassword存储

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-Mongooseuser 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插件通过serializeUserdeserializeUser支持序列化和反序列化操作。

更改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()
    }
}
...
原文地址:https://www.cnblogs.com/wydumn/p/12590664.html