接口安全/钩子

用户在前端调用后端接口是有安全风险的,开发者有责任管控接口以确保数据安全,不泄露给非授权用户,接口只开放给授权用户调用。数据是企业的重要资产,加强数据安全就是保护企业资产。

考虑要点

安全性

只有特定权限的用户才可执行特定操作,数据不被非授权用户篡改、删除,确保数据只在最小范围内可被操作。比如用户表数据只有管理员或用户本人才可以修改

机密性

只有特定权限的用户才可查看特定字段,不暴露给本不该看到数据的人,确保数据只在最小范围内可见。比如用户表里的手机号和身份证只有管理员或用户本人才能查看,其他人只能看到用户必要的非机密信息(昵称、头像等)

完整性

只有特定条件下才可执行特定操作,确保数据处于正确的状态、符合业务要求。比如生成会员编号有唯一性要求;再比如如果文章作者保存的是用户id,删除了用户表中此作者的数据会导致无法显示作者姓名与头像。

并发性

多人在同一时刻同时更新同一数据时可能会破坏数据。比如给文章点赞时,如果是在客户端在当前文章点赞数的基础上加一后保存回数据库,那么在高并发状态下数据就会互相覆盖而让点赞数明显偏少。如果是在服务器端用$inc操作符来给点赞数加一就可以避免这种错误。

接口的安全保障是通过两种钩子来实现的:拦截器和回调函数。
钩子函数可以绑定一个具体的接口,比如user.find;也可以绑定一个表对象,比如user,此时它管控的是user表下的所有接口;也可以绑定某个接口,比如find,此时它管控的是含有find接口的所有表对象。三种方式的优先级依次递减。

拦截器

调用接口前会先执行用户定义的拦截器函数。根据调用者权限修正参数甚至阻止接口调用。
有两个专门为此设计的函数okIf()ngIf()

okIf(条件, 字段, 排除)

符合就OK,马上执行接口不再执行剩余条件检查;不符就接着往下看
当条件成立允许当前用户立即执行该API而无需继续检查后续表达式,当条件不成立时继续检查后续表达式。
字段可选,是用逗号隔开的多个字段,如果传了则会只返回它指定的字段,但如果排除-1时则表示要在返回结果中排除前面指定的字段。

ngIf(条件, 警告消息)

符合就NG(Not Good,拒绝执行接口,马上返回前端;不符就接着往下看
当条件成立时拒绝当前用户执行该API而立即返回前端,当条件不成立时继续检查后续表达式。
如果后面的参数不传,则返回403(无权限)给前端。

通常前端要做过一次权限检查的,无权限的用户都应该看不到无权操作的按钮,不需要跑的后端来就立即得到了权限提示。这个函数是给安全兜底的,阻击另有目的的用户绕过前端黑到后端来。

ngIfstopIf不同:stopIf()仅仅是停止执行当前表达式块的后续表达式;而ngIf()仅用在后端API的执行前条件中,用来判断用户是否有权限执行此后端API,所以它不仅停止执行后续表达式,更重要的是不再执行后端服务。

回调函数

接口调用完后会执行,可修改返回值($R)或记录数据变更/日志处理等任务。

环境变量

接口参数

比如$user.get(id)里有变量id,$user.find(Q, O)里有查询条件Q和选项O。

table

表对象,包括系统表(user/order/asset)和自建业务表。

fx

表对象对应的接口。

me

接口的调用者(即前端登录用户$me),里面最常用到的字段是idrole。使用id可进一步从数据库获取此用户详细数据;role是角色数组,常用来判断用户的权限。另外iatissued at的缩写,是用户登录时给用户签发身份标识的时间(秒);expexpire的缩写,即用户身份标识的失效时间(秒),ai是用户所在的appId,提供跨站服务的时候作为验证用户来源的依据。

$O

原始数据(Original,未修改前的数据)。接口有idkey时参数时才有,比如get(),delete(),update()。
通常直接访问而无需带上$O,除非需要完整的原始数据。

$R

返回值(Return/Result)。回调函数才有。注意$R$r的区别。
通常直接访问而无需带上$R,除非需要修改返回值。

$exp

后端服务里的所有$exp表达式。

isUpdatingY

如果update()接口会更新y里的字段则isUpdatingY为真。

示例

ngIf(!me.role.includes("管理员") && auth != me.id, "没有权限")  // 也拒绝未登录直接调用,因为me不存在
okIf(me.role.includes("管理员") || auth == me.id)  // 如果调用者是管理员或数据创建者则立即放行去调用接口,不做进一步检查
ngIf(isUpdatingY)            // 任何人都不可以更新y字段内的数据(管理员或数据创建者已经在上一行放行,不在此语句的范围内) 
okIf(1, "phone, x.身份证", -1)                     // 任何人都不可以查看手机和身份证
okIf(1, "wx.nickname wx.headimgurl")               // 或者只让看到微信昵称和头像
O.select = "wx.nickname wx.headimgurl"             // 或者更改$user.find()里的选项参数



由众触低代码平台生成和驱动