-
Notifications
You must be signed in to change notification settings - Fork 396
10 如何启用身份验证
Passport 是与 Express 兼容的身份验证中间件。
Passport 提供了很多验证策略,iBlog2 中仅使用了 Local 作为后台管理员登录的验证中间件。下面对相关代码做一些梳理:
$ npm install --save passport
$ npm install --save passport-local
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 发起登录请求,注意发送的数据 data
中 UserName
,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');
});