Skip to content

10 如何启用身份验证

孙正华 edited this page Jul 26, 2018 · 3 revisions

Passport 是与 Express 兼容的身份验证中间件。
Passport 提供了很多验证策略,iBlog2 中仅使用了 Local 作为后台管理员登录的验证中间件。下面对相关代码做一些梳理:

安装 passport

$ npm install --save passport
$ npm install --save passport-local

初始化 passport

https://github.com/eshengsky/iBlog2/blob/master/app.js#L69-L70

app.use(passport.initialize());
app.use(passport.session());

服务端验证配置

这里的验证逻辑是从 /config/account.json 文件中读取管理员账号密码进行核对,实际情况下一般从数据库读取。

https://github.com/eshengsky/iBlog2/blob/master/routes/auth.js#L10-L28

passport.use(new Strategy({
    // 页面上的用户名字段的name属性值
    usernameField: 'UserName',

    // 页面上的密码字段的name属性值
    passwordField: 'Password'
},
    (username, password, cb) => {
        const account = require('../config/account');

        // 自己判断用户是否有效
        if (username === account.UserName && password === account.Password) {
            // 验证通过
            return cb(null, account);
        }

        // 验证失败
        return cb(null, false);
    }));

为保证登录状态的持久,passport 会将用户信息序列化和反序列化到 Session 中。而具体的逻辑需要自己去实现。
这是一个最简单的序列化实现,用户对象的 Id 属性会被存入 Session.

https://github.com/eshengsky/iBlog2/blob/master/routes/auth.js#L30-L32

passport.serializeUser((user, cb) => {
    cb(null, user.Id);
});

反序列化时再根据 Session 中存放的用户 Id 获取用户对象。

https://github.com/eshengsky/iBlog2/blob/master/routes/auth.js#L34-L40

passport.deserializeUser((id, cb) => {
    const account = require('../config/account');
    if (account.Id === id) {
        return cb(null, account);
    }
    return cb(err);
});

前端发起登录请求

这里使用 jQuery.ajax 发起登录请求,注意发送的数据 dataUserName,Password 这2个属性名要与上文服务端验证配置中的保持一致。

https://github.com/eshengsky/iBlog2/blob/master/public/js/account.js#L56-L60

$.ajax({
    url: "/login",
    type: "Post",
    data: {UserName: userName, Password: password},
    success: function (data) {
    }
});

服务端接收登录请求

使用 passport.authenticate 基于上文提到的验证配置进行身份验证。注意 req.session.returnTo 会保存登录前访问的页面 Url.

https://github.com/eshengsky/iBlog2/blob/master/routes/auth.js#L56-L86

// 提交登录请求
router.post('/login', (req, res, next) => {
    passport.authenticate('local', (err, user, info) => {
        if (err) {
            next(err);
        } else if (!user) {
            logger.errLogger(new Error(res.__('auth.wrong_info')), req);
            res.json({
                valid: false,
                message: res.__('auth.wrong_info')
            });
        } else {
            // 登录操作
            req.logIn(user, err => {
                let returnTo = '/admin';
                if (err) {
                    next(err);
                } else {
                    // 尝试跳转之前的页面
                    if (req.session.returnTo) {
                        returnTo = req.session.returnTo;
                    }
                    res.json({
                        valid: true,
                        returnTo
                    });
                }
            });
        }
    })(req, res, next);
});

确保用户已登录

使用 connect-ensure-login 验证中间件可以确保指定的路由必须要登录后才能访问。
安装 connect-ensure-login:

$ npm install --save connect-ensure-login

https://github.com/eshengsky/iBlog2/blob/master/app.js#L84-L86

// 后台站点路由,需要身份验证
app.use('/admin', require('connect-ensure-login')
    .ensureLoggedIn('/login'), admin);

方法 ensureLoggedIn() 可以传入一个 Url 作为参数,当检测到用户未登录时自动跳转到指定的登录页面,若没有传入该参数则默认为 '/login'

退出登录

https://github.com/eshengsky/iBlog2/blob/master/routes/auth.js#L88-L93

// 退出登录
router.post('/logout',
    (req, res) => {
        req.logout();
        res.redirect('/login');
    });