方法安全防护的最佳实践是什么?是否所有方法都需做输入校验?
方法安全防护最佳实践问题解答
核心结论是不需要所有方法都做全量防护,是否要给insert加空列表校验,取决于你这个方法的公开范围和团队约定:
两种适配场景的选择
- 如果
Service类属于当前业务模块私有组件,且团队有明确约定insert方法仅能由Endpoint的create方法调用,完全可以保留现有写法,但需要补充两个前置保障:- 在
insert方法的注释里明确标注前置约束:调用方必须保证传入的List非空,空列表调用会抛出未捕获异常 - 有条件的话优先用类型系统替代运行时校验,比如把参数类型从
List[Item]改成NonEmptyList[Item],从编译层面就杜绝空列表传入的可能,既没有多余的样板代码,也避免后续有人误调用。
- 在
- 如果
Service是公共基础组件、后续会开放给其他业务模块调用、或者团队人员流动性大没有强约束,必须加校验。你现在省了几行校验代码,后续其他人不知道要做前置校验直接传空列表,出故障的排查和修复成本远高于你写校验的成本,属于典型的捡芝麻丢西瓜。
关于样板代码冗余的顾虑
- 边界层(比如你示例里的Endpoint,属于对外暴露的请求入口)必须做全量的输入合法性校验,内部方法可以靠信任链传递校验结果,不用重复做相同的校验逻辑,避免样板代码爆炸。
- 注意区分安全防护和普通参数校验:涉及权限校验、防注入、防数据篡改这类安全规则,不管是哪层的方法都要做;普通的参数格式、非空这类校验,只要在最外层做一次即可,内部靠约定或者类型约束保障。
针对你给出的示例的具体建议
// 最优方案:用类型约束从编译层解决问题,不需要重复校验 class Endpoint { def create(list: List[Item]): Response = list match { case Nil => BadRequest("Empty list") case nonEmptyList: NonEmptyList[Item] => service.insert(nonEmptyList) } } class Service { // 参数是NonEmptyList,编译层面就不允许空列表传入 def insert(list: NonEmptyList[Item]) = list.head ... }
如果不方便改参数类型,要么给insert方法加1行校验抛出明确的业务异常,比默认的集合空访问异常更容易排查问题:
def insert(list: List[Item]) = { if(list.isEmpty) throw new IllegalArgumentException("insert method requires non-empty list") list.head }
要么就把insert方法改成模块私有,从访问权限上限制调用方,避免无关人员误调用。
内容的提问来源于stack exchange,提问作者Veidt
相关产品推荐
相关产品推荐

