乐趣区

基于阿里egg框架搭建博客4权限控制

相关文章

基于阿里 egg 框架搭建博客(1)——开发准备
基于阿里 egg 框架搭建博客(2)——Hello World
基于阿里 egg 框架搭建博客(3)——注册与登录
基于阿里 egg 框架搭建博客(4)——权限控制
基于阿里 egg 框架搭建博客(5)——置顶导航条
基于阿里 egg 框架搭建博客(6)——浏览、发表文章
基于阿里 egg 框架搭建博客(7)——编辑文章

git

https://github.com/ZzzSimon/e…
喜欢就点个赞吧!

正文

上一篇文章我们实现了用户的注册与登录,接下来就需要对用户权限进行控制了,比如:普通用户只能评论,管理员可以发表文章,最高管理员可以修改用户权限等等。
由于权限控制是一个通用的功能,我们把这块功能做成中间件。关于中间件:

官方文档:https://eggjs.org/zh-cn/basic…

功能设计

  1. 一个用户对应 1 个 角色
  2. 可以通过配置文件配置,某一个角色 无权限 使用的页面与接口
  3. 可以配置无需验证用户与权限的 path。比如:登录与注册的相关页面与接口
  4. 只有登录过才能访问的 path,否则跳转登录页。

User 表,增加 role(角色)字段

配置文件

我们在 config/config.default.js 中加入以下内容:

        auth : {noAuth:['/login.htm','/user/login','/register.htm','/user/register'],
            noPermission:{admin:[],
                manager:['/admin.htm'],
                user:['/admin.htm','/edit.htm']
            }
        }

其中:
noAuth节点配置的是无需验权就能访问的 path
noPermission节点配置的是各个角色 无权限 访问的 path

auth.js 中间件代码

我们创建 app/middleware/auth.js 文件:

module.exports = (options, app) => {return async function auth(ctx, next) {
        // 如果用户 session 没失效
        if (typeof (ctx.session.user) !== 'undefined') {
            const username = ctx.session.user.username;
            // 这里有两种做法,第一种每次都查库校验角色,优点:实时,角色变更对用户无感。缺点:查库效率低,可考虑用 redis
            // 第二种,把角色信息放进 session, 优点:无需查库,效率高。缺点:角色变更时需额外逻辑来处理老的 session,否则客户端的用户角色无法实时更新
            const role = await ctx.service.user.getRoleByUsername(username);
            const noPerList = options.noPermission[role];
            if (noPerList && !noPerList.includes(ctx.path)) {await next();
            } else {ctx.body = '无权限,请联系网站管理员!';}
            // 登录注册页面不需要权限
        } else if (options.noAuth.includes(ctx.path)) {await next();
            // 如果 session 失效后则重定向到登录页
        } else {ctx.redirect('/login.htm')
        }
    }
};

效果

我们创建一个用户,并给与他 user 角色,由配置文件可以看出,user 角色无权限访问 /edit.htm 路径。如图:

正则匹配

如果页面也来越多,或者有些带参数的 path 是动态的,我们需要一定的规则来过滤 path。这时候就需要用到正则匹配,我们修改 auth.js 文件:

module.exports = (options, app) => {function isNoPer(noPerList,path) {for (let i = 0;i<noPerList.length;i++){const patt=new RegExp(noPerList[i]);
            if (patt.test(path)) {return true;}
        }
        return false;
    }
    return async function auth(ctx, next) {
        // 如果用户 session 没失效
        if (typeof (ctx.session.user) !== 'undefined') {
            const username = ctx.session.user.username;
            // 这里有两种做法,第一种每次都查库校验角色,优点:实时,角色变更对用户无感。缺点:查库效率低,可考虑用 redis
            // 第二种,把角色信息放进 session, 优点:无需查库,效率高。缺点:角色变更时需额外逻辑来处理老的 session,否则客户端的用户角色无法实时更新
            const role = await ctx.service.user.getRoleByUsername(username);
            const noPerList = options.noPermission[role];
            if (noPerList && !isNoPer(noPerList,ctx.path)) {await next();
            } else {ctx.body = '无权限,请联系网站管理员!';}
            // 登录注册页面不需要权限
        } else if (options.noAuth.includes(ctx.path)) {await next();
            // 如果 session 失效后则重定向到登录页
        } else {ctx.redirect('/login.htm')
        }
    }
};

这时候我们的配置文件就可以支持正则表达式了:

        auth : {noAuth:['/login.htm','/user/login','/register.htm','/user/register'],
            noPermission:{admin:[],
                manager:['/admin.htm'],
                user:['/admin.htm','/edit.*']
            }
        }

结尾

如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!

退出移动版