下面可以去设计一个做访问控制用的中间件,在需要做访问控制的接口上面可以使用这个中间件去检查用户的权限,比如检查一下用户是不是拥有他要访问的这个内容资源。这就需要这个中间件可以接收额外的参数,在接口上使用这个中间件的时候可以配置一下参数的值,说明一下需要检查的权限。
打开 src/auth/auth.middleware,先在这个文件的顶部可以从 auth.service 里面,导入刚才定义的 possess 这个服务方法。
然后在这个文件里再找个地方定义访问控制用的中间件。这个中间件会支持一个额外的选项参数,可以先定义一个 interface 描述一下这个参数的类型。名字是 AccessControlOptions ,在这个 interface 里面添加一个可选的 possession 属性,类型是 boolean。
下面可以再导出一个函数,名字叫 accessControl,接收一个参数,名字是 options,把这个参数的类型设置成 AccessControlOptions
然后让这个函数返回另一个函数,这样在它返回的这个函数的内部就可以使用 options 这个参数了。使用这个方法我们可以让中间件支持额外的参数。
return 一个异步函数,它接收几个参数,有 request,类型是 Request,还有 response,类型是 Response,还有一个 next,类型是 NextFunction。
在这个函数的内部,可以先在控制台上输出一行文字,访问控制。 下面可以解构一下选项参数的值,把它里面的 possession 解构出来。
这个中间件要配合 authGuard 中间件一块儿使用,把这个中间件放在 authGuard 中间件的后面,这样就可以在这个中间件里得到当前用户相关的数据了。从当前用户里面,把 id 解构出来,重命名为 userId。
我们可以认为 ID 字段的值等于 1 的这个用户是应用的超级管理员,不受任何限制,所以这里可以先判断一下当前用户的 ID ,看看是不是等于 1,如果是我们就直接放行,可以 return next()。
下面要准备一些资源相关的东西。添加一个 resourceIdParam,在 request.params 里面会包含所有的地址参数,这些地址参数是在定义接口的时候设计好的,使用这个接口的时候可以设置接口的地址参数的值。比如定义接口的时候如果设置的地址是 /posts/:postId,使用这个接口的时候地址可以设置成 /posts/3,这样在接口上用的中间件或者处理器里面,request.params 里面就会包含一个 postId 属性,对应的值就是 3 。
所以我们可以根据 request.params 里的这个地址参数的名字,知道资源的类型还有资源的 ID。可以先用 Object.keys() 这个方法,获取到一个对象里所有的属性的名字,然后把这组属性名字的第一个项目交给 resourceIdParam。
如果访问的是 post 这种资源,这种资源相关的接口里面一般会包含 :postId 参数。所以这个 resouceIdParam 的值应该就是 "postId" 。下面要处理一下这个字符串,去掉它里面的 Id,剩下的部分就可以表示资源的类型。
添加一个 resourceType,它的值用一下 resourceIdParam 这个字段串上的 replace 方法,替换掉字符串里的 Id 。剩下字符就可以作为资源的类型的名字。
知道了资源的类型以后还得知道资源的 ID,可以这样,添加一个 resourceId,用 parseInt 处理一下,request.params 方括号 resourceIdParam ,得到的就是资源的 ID ,把它转换成 10 进制的数字。
下面要去检查资源拥有权,可以先判断一下,看看 possession 是不是真的,就是如果在接口上使用 accessControl 中间件的时候,把它的选项参数里的 possession 属性的值设置成 true 的话,我们就检查当前用户对指定资源的拥有权。
用一组 try,catch,要把做的事情放在 try 这个区块里面,添加一个 ownResource,等于,await,等待执行 possess 返回的结果。一个对象参数,里面需要 resourceId ,resourceType 还有 userId 这几个属性。
判断一下,如果 !ownResource,意思就是如果用户不拥有这个资源,就可以触发一个异常,return next,带一个错误,设置一下错误信息,USER_DOES_NOT_OWN_RESOURCE
再处理一下在 try 区块里做事情的时候遇到的异常情况,return 执行 next() 带着 error 。
在这个中间件里面,需要再执行一下 next(),继续下一步。 复制一下这个错误信息,在异常处理器那里可以处理一下这种异常情况。
打开 src/app/app.middleware ,复制一份情况,如果错误信息是这个 USER_DOES_NOT_OWN_RESOURCE ,就可以把响应的状态码设置成 403,表示 Forbidden ,错误信息设置成 您不能处理这个内容