eggjs
是阿里开源的一个针对企业级应用的nodejs
框架,最近在学习eggjs
中的passport
,走了一些弯路,写一点心得,使用local
策略实现本地登录。
安装相关的依赖
npm i --save egg-passport egg-passport-local
开启插件
config\plugin.js
中
exports.passport = {
enable: true,
package: 'egg-passport',
};
exports.passportLocal = {
enable: true,
package: 'egg-passport-local',
};
配置用户名密码字段
config\config.default.js
中
config.passportLocal = {
usernameField: 'username',
passwordField: 'password',
};
配置验证用户以及序列化与反序列化
app.js
中,添加逻辑
app.passport.verify(async (ctx, user) => {
// 查找数据库,并验证密码是否正确
/*
const User = ctx.model.User;
let foundUser = await User.findOne({ username: user.username });
if(!foundUser || !foundUser.encryptPassword(user.password)) return false;
return foundUser;
*/
return user;
});
// 序列化与反序列化,序列化存储到session中只保存用户id
app.passport.serializeUser(async (ctx, user) => {
// return pick(user, ['id', 'name', 'username', 'email']);
return user;
});
app.passport.deserializeUser(async (ctx, user) => {
return user;
});
创建控制路由
在controller
文件夹中,创建home.js
,添加控制路由
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
const ctx = this.ctx;
ctx.body = `
<div>
<h2>${ctx.path}</h2>
<a href="/admin">admin</a>
</div>
`;
}
async admin() {
const { ctx } = this;
if (ctx.isAuthenticated()) {
console.log('ctx.user', ctx.user);
// show user info
ctx.body = `
<div>
<h3>You login</h3>
<a href="/logout">Logout</a>
</div>
`;
} else {
// redirect to origin url by ctx.session.returnTo
ctx.session.returnTo = ctx.path;
// await ctx.render('login.html');
ctx.redirect('/login');
}
}
async login() {
await this.ctx.render('login.html');
}
async logout() {
const ctx = this.ctx;
ctx.logout();
ctx.redirect(ctx.get('referer') || '/');
}
async register() {
await this.ctx.render('register.html');
}
async doRegister() {
const ctx = this.ctx;
const User = ctx.model.User;
console.log('model is ', User);
const requestBody = ctx.request.body;
const user = new User(requestBody);
user.provider = 'local';
const result = await user.save();
console.log('result is ', result);
ctx.redirect('/');
}
async loginCallback() {
let { ctx } = this;
if (ctx.isAuthenticated()) {
ctx.body = {
code: 0,
data: ctx.user,
msg: '',
};
} else {
ctx.body = {
code: -1,
data: null,
msg: '用户名或密码错误'
}
}
}
}
module.exports = HomeController;
配置路由
router.js
中,配置
module.exports = app => {
const { router, controller } = app;
let { home } = controller;
router.get('/', home.index);
router.get('/admin', home.admin);
router.get('/logout', home.logout);
router.get('/login', home.login);
const localStrategy = app.passport.authenticate('local', {successRedirect: '/loginCallback', failureRedirect: '/loginCallback'});
router.post('/passport/local', localStrategy);
router.post('/passport/local', home.localPassport);
router.get('/loginCallback', home.loginCallback);
router.get('/register', home.register);
router.post('/register', home.doRegister);
};
在这里,localStrategy
的定义中,添加了 successRedirect
和failureRedirect
并且指向了同一个路由,在loginCallback
中,判断是否登录,并返回对应的json数据,这种是为了处理ajax
请求的情况。
app.passport.authenticate('local', {successRedirect: '/loginCallback', failureRedirect: '/loginCallback'})
前端代码
app/view
中
login.html
<html>
<head>
<title>Login</title>
<script type="text/javascript" src="/public/lib/js.cookie.js"></script>
<script type="text/javascript" src="/public/lib/jquery-1.12.4.min.js"></script>
<script>
$(function(){
// 对于所有的ajax请求,请求头添加cookie标识
var csrftoken = Cookies.get('csrfToken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader('x-csrf-token', csrftoken);
}
},
});
});
</script>
</head>
<body>
<form method="post" id="loginForm">
<input type="text" name="username" placeholder="用户"/>
<input type="password" name="password" placeholder="密码"/>
<input type="submit" value="提交"/>
</form>
<div>
没有账号?<a href="/register">注册</a>
</div>
</body>
<script type="text/javascript">
$('#loginForm').on('submit', function(e) {
e.preventDefault();
let username = this.username.value.trim();
let password = this.password.value.trim();
$.ajax({
url: '/passport/local',
method: 'post',
data: {
username: username,
password: password
}
}).then((res, statusCode, xx) => {
console.log('res ', res, statusCode, xx);
console.log()
}, error => {
if(error.status === 401) {
alert("账号或密码错误");
}
console.warn('error ', error);
})
});
</script>
</html>
Done!