關於中間件
https://eggjs.org/zh-cn/basics/middleware.html
官方文檔說的很清楚了,不再敘述。
我們要達到怎么樣一個效果?
- 用戶沒有登錄不能訪問一些特定的頁面,比如修改密碼、修改資料啊這些敏感操作。如果用戶沒有登錄訪問這些頁面會自動跳轉到登錄頁面讓用戶登錄。
- 如果用戶登錄過了就可以訪問這些頁面(驗證通過。)
- 沒有登錄可以訪問登錄頁面來進行登陸,或者注冊等不需要權限的頁面。
如果不使用中間件你會怎么寫
在controller/user 修改密碼,
async changePassword(){
if (this.ctx.session.userId) { // 如果有這個session
// 執行修改密碼
} else {
// 不寫就沒有響應,會404
ctx.redirect('/login');
}
}
然后修改資料
async changeUserInfo(){
if (this.ctx.session.userId) { // 如果有這個session
// 執行修改資料
} else {
// 不寫就沒有響應,會404
ctx.redirect('/login');
}
}
然后登錄就不用判斷
async login(){
let {userName, password} = this.ctx.request,body;
// 校驗密碼
let userFind = this.service.findOne({userName, password});
// 獲取user信息
if (userFind) {
this.ctx.session.userId = userFind._id;
// 返回成功
this.ctx.body = '登錄成功';
} else {
this.ctx.body = '登錄失敗,賬號密碼錯誤';
}
}
這樣如果代碼量小的話也能接受,但是如果將來接口越來越多,需要檢驗權限的地方也越來越多,修改就會很麻煩。
剝離出來,自成體系
在app/middleware下面新建authLogin.js文件用來判斷是否登錄
module.exports = (options, app) => {
return async function testMiddleware(ctx, next) {
let whiteUrls = options.whiteUrls || [];
// 如果ctx.url在白名單中
let isWhiteUrl = whiteUrls.some((whiteUrl)=> ctx.url.startsWith(whiteUrl));
if (! isWhiteUrl) {
console.log('authLogin');
if (! ctx.session.userId) {
ctx.redirect('/login'); // 讓用戶去登錄
}
else {
console.log('auth ok');
await next();
}
} else {
// 白名單
console.log('white url');
await next();
}
};
};
在controller/user 修改密碼,
async changePassword(){
//不需要判斷,直接執行修改密碼
}
然后修改資料
async changeUserInfo(){
//不需要判斷,直接執行修改資料
}
然后登錄還是一樣
async login(){
let {userName, password} = this.ctx.request,body;
// 校驗密碼
let userFind = this.service.findOne({userName, password});
// 獲取user信息
if (userFind) {
this.ctx.session.userId = userFind._id;
// 返回成功
this.ctx.body = '登錄成功';
} else {
this.ctx.body = '登錄失敗,賬號密碼錯誤';
}
}
代碼是不是精簡清爽多了呢?
注意的幾個點,
- 要加到config的middleware列表里面:
config.middleware = [''authLogin'];
- await next()要放在最后,這樣意味着校驗規則會在路由匹配之前執行。
- whiteUrl是在config.default.js中的options配置,也可以不要這個,直接使用match或者ignore(相關規則參考官方文檔關於中間件這一塊)
config.authLogin = {
whiteUrls: ['/test'], // 是使用url的前綴匹配的
// 不需要登錄的頁面,白名單URL
// 也可以使用
ignore: ['/login', '/register', '/doLogin', '/doRegister']
// 使用 match是限制只在這幾個頁面執行
// match和ignore不能同時使用
};
- 不配置 config.authLogin的話呢?只在特定路由中使用:
router.get('/login', authLogin, controller.user.login);
