Claims-based authorization,基于声明的权限检查。基于用户角色的权限检查描述了主体是什么,这里说的主体指的就是用户。基于声明的权限检查描述的是主体能做什么。
比如我们可以在应用里给用户分配一些能做的事情,这些能做的事情就是用户的一些权限,然后在接口的处理器那里可以设置要求的权限。实现这种权限检查跟之前基于用户角色的权限检查差不多。
装饰器
下面我们先去创建一个装饰器来设置接口要求的权限,执行 nest g d permissions ,生成一个叫 permissions 的装饰器。
守卫
然后再生成一个检查用户权限的守卫。执行 nest g gu permissions,生成一个名字是 permissions 的守卫。
注册
在项目里,打开 app.module,在应用的全局注册一个守卫,放在 providers 里面,添加一个对象,里面有个 provide 属性,值是 APP_GUARD,再添加一个 useClass,值是刚才创建的 PermissionsGuard 这个守卫。
控制器
打开 app.controller 这个控制器,在里面定义一个新的接口,用 @Get 装饰一下接口处理器,接口地址是 jurassic-park ,再用 @Permissions 装饰一下,提供一个 visit_jurassic_park,这个参数值就是这个处理器声明要求的的用户权限。
添加一个方法,名字是 getJurassicPark,返回的值是一只恐龙。
permissions.guard
打开 permissions.guard, 在这个守卫类里面,添加一个构造方法,里面添加一个属性,private reflector,类型是 Reflector。
在 canActivate 里面,声明一个 requiredPermissions,它的值用一下 this.reflector.getAllAndOverride,要得到的元数据是 permissions,提供一个数组,里面添加一个 context.getHandler() 。
下面判断一下,!requiredPermissions,如果没有设置要求的权限,可以 return true。
再解构一下请求,我们假设用户的权限是在请求的头部里,真实的应用,应该把用户权限存储在某些安全的地方,比如应用的数据仓库里。这里为了演示我们直接让用户可以在请求的时候设置自己的权限。
需要的 headers,继续解构它,需要的是 x-user-permissions ,命名为 userPermissions。 等于 context.switchToHttp ,继续调用 getRequest。
判断一下,!userPermissions,如果没有权限,可以 return false。
最终 return 的值,可以用一下 requiredPermissions.some ,当前项目是 permission,类型是 string,返回的值可以用一下 userPermissions.split,按逗号分成一个数组,调用 includes,把 permission 交给这个方法。
测试
在 http 客户端,可以测试一下,用 Get 方法,请求 jurassic-park 这个接口,直接访问一下,会得到 403 响应,因为当前用户没有接口处理器要求的 visit_jurassic_park 这个权限。
在请求里添加一个头部,名字是 X-User-Permissions,值可以是 visit_jurassic_park ,再访问一下,这次就得到了一只恐龙,因为当前用户拥有接口处理器要求的权限。