新手刚学nodejs 关于express的权限控制有问题请教各位大神
发布于 9 年前 作者 HengCC 16745 次浏览 最后一次编辑是 8 年前 来自 问答

花了些时间初步了解了nodejs 和express 的运作方式.这几天准备做一个小的项目,在考虑到权限问题的时候却被卡住了.

我的需求是这样的. 单纯的用户认证这个应该很简单,passport这些都能胜任… 但是我想做的是针对每个请求URL进行权限控制.比如针对普通用户,

普通用户可以访问/user/updateSelf ,不能访问/user/addUser,而作为管理员可以访问任何一个链接. 这是第一方面

另外一方面,因为express也支持 restful的请求, 类似/user/:userId, /user/:action/:data ,如果采用这样的写法, 那么后台实际定义的URL是上面写的这样, 而接收过来的请求

URL 可能是 /user/12 , /user/save/u3 , 针对这样的URL 我又该怎么进行权限的控制呢.

我打算将所有定义的URL都和数据中的角色表进行映射, 比如 admin 对应 [/user/:userId,/user/:action/:data] 而普通用户角色 user对应 [/user/:userId]

那么 对于这样的路由 app.get(’/user/:action/:data’,function(res,req,next){…}) user就不会有权限访问.

但是我又该如何在处理请求之前获取 这些实际的url 诸如 /user/1 对应的是 /user/:userId 这个Url呢?

java中有反射,并且像struts这样的框架提供了拦截器,在执行action之前可以反射获取待执行的action,然后在判断URL和角色有无权限. 在nodejs中有这样的办法吗?

有人有好的办法吗 或者 有没有其他的权限控制框架. 让我不用为每个路由去配置权限. 而只要通过aop的方式,在执行所有的路由方法之前 判断权限?

19 回复

我说一种比较常见的做法.

// 写一个验证middleware
var auth = function(req, res, next) {
  var routePath=req.route.path;
 // ....  
}
app.get('/user/:action/:data’, auth,  function(req, res, next) {
  //...
});
app.get('/user/:userId’, auth,  function(req, res, next) {
  //...
});

@William17 这个我之前考虑过. 因为这样的写法还是得为每个路由手动在路由的声明上配置一个auth

因为路由是支持写多个function的. 所以我想有没有这样一种可能

假设原先路由是这样定义的. app.get(’/user/:action/:data’, function(req, res, next) { //… }); app.get(’/user/:userId’, function(req, res, next) { //… });

那么有没有像java中反射那样的机制来获取我所定义路由中的分别对应的function或者function数组. 这样我只需要在初始化路由的时候.为每个路由都push一个或者多个新的权限认证函数进去,类似你的auth.

比如 获取路径对应路由定义的 function = app.getRoutefunctions() 返回类似{path:’/user/:userId’,functions:[…一系列function]} 最后我只要遍历这个对象 functions.push(auth),这只是举例. 实际不知道有没有办法完成这样的功能. 如果能达到这样 我就不需要为每个路由都配置auth了

https://cnodejs.org/topic/551802d3687c387d2f5b2906#56069ca9148959375f34ff37 这边文章中说的和我的想法基本一样. 博主也提到了一个框架 sails 以及其中的sails-permissions

express中有无类似的中间件呢?

直接代码说话~

    router.post('/post', checkLogin);
    function checkLogin(req, res, next) {
      if (!req.session.user) {
        req.flash('error', '未登录!');
        res.redirect('/login');
      }
      next();
    }

@Tei320 兄弟, 这样还是得为每个路由配置checkLogin. 我是想要那种aop 通过切面的方式注入进去那种方式,而不用为每个路由配置认证和权限.

那你这相当于要java里struts2的filter,那种貌似在session中存储权限列表,express里就在路由前加个中间件了

app.use(function(req, res, next) {
	//判断当前访问url是否在权限列表
});

aop不是通常用来管理事务么…

@luoyjx 也有用到拦截器啊,能获取控制器对象。

@luoyjx @zh-h 我就是想要拦截器的那种概念,要获取到控制器对象. 按照 @luoyjx 的想法.我是获取不到我要的控制器对象的.

这样restful形式的URL 我就没有办法获取到路由中定义的URL,比如/user/1 对应的/user/:userId 我只能获取到/user/1 而获取不到/user/:userId

另外我这样的想法确实是想吧权限放在session中, 每次执行都去判断权限.

@HengCC 这个也是中间件的一种呀,代码量虽然增加啦,但这个是自己可控,类似1楼的方法,在需要的地方添加。你可以直接用app.use自己写一个中间件也同理,但是用app.use真的是对每一个路由都验证,有些地方并不需要

@HengCC Token认证: if ((req.url.in****dexOf(‘admin’) >= 0 && dbUser.role == ‘admin’) || (req.url.indexOf(‘admin’) < 0 && req.url.indexOf(’/api/v1/’) >= 0)) { next(); // To move to next middleware } else {

http://thejackalofjavascript.com/architecting-a-restful-node-js-app/

以下代码片段应该是你要的效果: var myApp = angular.module(‘ngclient’, [‘ngRoute’]);

myApp.config(function($routeProvider, $httpProvider) {

$httpProvider.interceptors.push(‘TokenInterceptor’);

$routeProvider .when(’/login’, { templateUrl: ‘partials/login.html’, controller: ‘LoginCtrl’, access: { requiredLogin: false } }).when(’/’, { templateUrl: ‘partials/home.html’, controller: ‘HomeCtrl’, access: { requiredLogin: true } })

开源博客系统liblog已经具备权限管理,方便可以自定义,并且开放源码,可以参照他看一下http://livisky.github.io/

我借鉴的是java中shiro框架的原理,数据库中存 role(角色表),func(权限表),user(用户表), role_func(角色权限的关联表), user_role(用户角色关联表),可以实现为不同的角色动态添加权限,然后在func表中添加每一个路由作为一个权限,自己写中间件进行验证,对于你说的/user/:userId类似于这种问题,在中间件中对于请求比如说 /user/23,将其中的数字替换成:userId,然后对照数据库检查中用户是否有这个权限,中间件做为权限校验

@Yhaojing 有没有源码开放,学习一下,谢谢

function(req, res, next){
	console.log(req.route.path)
}

是可以得到/user/:id?的 express框架其实很多api都暴露出来了,比如app对象,就挂载在req.app上

所以呢,大概有两种方式实现你要的效果

胆子小一点:

把controller原始url表达式和对应的auth模块做一个映射公共方法 写的每一个路由函数里,根据req.route.path进行调用鉴权;

胆子大一点:

修改express框架源码里面的lib/router/route.js,大致看了下源码,在189行处,添加如下的代码:

...	
var handles = flatten(slice.call(arguments));
//添加的代码如下:
var authArr = getAuth(this.path);
handles = authArr.concat(handles);
...

getAuth就是你自己写的根据设置的path规则返回的鉴权函数,此处需要返回一个数组,如下所示:

function getAuth(path){
	if(path === '/user/:usrId?')
		return [function auth1(){/*鉴权函数1*/}, function auth2(){/*鉴权函数2*/}];
	//如果没有匹配到,则返回空数组,表示无需鉴权
	return [];
}

这样就等于在每一个业务开发者编写的路由中,自动根据path注入你的映射公共方法,并且不同的路由可以注入不同的鉴权方法(一个或者多个),这样对于业务模块编写者来说感知不到,易用性会好一点

我是这样做的,写一个auth中间件,然后数据库中存一个 role字段,对应 普通用户和管理员等,然后用户登录的时候 设置session.role = ?, 然后在每个路由上加上auth (if req.session = Xxx) {//…}(就可以了

@SuperZee 和你一样的做法,我是用的hapi框架不是express,写一个auth插件,数据库中存一个role字段, 可以配置admin,monitor, app等等细分权限,然后路由规则也是以权限名作为url开头,用户登录的时候将role信息存到 localStorage,在服务器端路由中判断该路由名字的权限是否为真。

你这种情况需要用角色权限控制,每个登录用户拥有一个角色,每个角色对应一组权限也就是可以访问的url,如果角色的权限可以动态改变就需要把角色存库来动态获取,其实也就是楼上有人说的在user中加一个role字段来控制权限

回到顶部