zoukankan      html  css  js  c++  java
  • 谈谈防御性编程

    谈谈防御性编程

    一提到“防御性编程”,大家都会感觉,这个话题很大,不知从何说起,该说哪些具体内容。

      我做这篇文章的源头,是我已经做了很多很多相关前端及后端数据流校验的事情。对测试同学给我提出的所有bug做了整体的统计促使我对“防御性编程”的思考。我想绝大多数的程序bug都是因为代码“防御性”做得不够好而导致的,而导致代码“防御性”不够完善的原因又是多种多样的。

      我们做防御性编程,无非一个目的——打造高质量的模块(或程序)。然而,相信大家都清楚,不可能仅凭做好了“防御性”来评价模块(或程序)的质量高与低。同样的,即使我们搭建了十分健壮的代码“防御性”,我们也不可能确保不会有任何bug的产生。

      “防御性编程”——它更多的,是一种努力。

      一、理解之

      引入一篇文章——什么是防御性编程,它对“防御性编程”的定义——

    防御性编程是一种细致、谨慎的编程方法。为了开发可靠的软件,我们要设计系统中的每个组件,以使其尽可能地“保护”自己。我们通过明确地在代码中对设想进行检查,击碎了未记录下来的设想。这是一种努力,防止(或至少是观察)我们的代码以将会展现错误行为的方式被调用。

    防御性编程是一种编程习惯,是指预见在什么地方可能会出现问题,然后创建一个环境来测试错误,当预见的问题出现的时候通知你,并执行一个你指定的损害控制动作,如停止程序执行,将用户重指向到一个备份的服务器,或者开启一个你可以用来诊断问题的调试信息。

      再次思考这个定义,其实它真正要做的,是“当预见的问题出现的时候通知你,并执行一个你指定的损害控制动作”。而对于的性质,可以理解为“是一种努力,是一种编程习惯”。

      而对于“防御性编程”的作用,其中的两张图足以说明——

      以前可能这样做——

      

      现在可能这样做——

      

      

      “防御性编程”帮助我们从一开始就编写正确的软件,而不再需要经历“编写-尝试-编写-尝试……”的循环过程。

    当然,防御性编程并不能排除所有的程序错误。但是问题所带来的麻烦将会减少,并易于修改。防御性程序员只是抓住飘落的雪花,而不是被埋葬在错误的雪崩中。

    防御性编程是一种防卫方式,而不是一种补救形式。

      OK,对于“防御性编程”概念的理解就说到这儿。不得不多说的一点是——每种防御性的做法都需要一些额外的工作,从而导致它降低了代码的效率。那么,具体有哪些去做“防御性编程”的技巧呢?

      二、使用之

      引入文章——防御性编程技巧,直接枚举出里面的技巧——

    复制代码
    1> 使用好的编码风格和合理的设计
    2> 不要仓促地编写代码 
    3> 不要相信任何人 
    4> 编码的目标是清晰,而不是简洁
    5> 不要让任何人做他们不该做的修补工作 
    6> 编译时打开所有警告开关 
    7> 使用静态分析工具 
    8> 使用安全的数据结构
    9> 检查所有的返回值 
    10>审慎地处理内存(和其他宝贵的资源) 
    11>在声明位置初始化所有变量
    12>尽可能推迟一些声明变量 
    13>使用标准语言工具 
    14>使用好的诊断信息日志工具 
    15>审慎地进行强制转换 
    16>其他 
            a> 审慎地进行强制转换 
            b> 提供默认的行为 
            c> 检查数值的上下限
    复制代码

      读到每一点,相信共性的问题,大家都亲身接触过,相信大家都有不同的感悟与理解。随着经验的积累,相信大家对具体问题如何去做“防御性”的方式也是不同的。总之,都是对程序的一种努力。

      三、数据流处理

      都知道“防御性编程”的范围很大,在这里,我想对其中的一点,做深入的研究说明——数据流处理。

      拿web请求的流程来讲,用户首先从浏览器端输入数据、触发请求,到服务器端程序接收数据、处理业务逻辑,再到DB库对数据的持久化存储。整个数据流走下来,都需要经历“风风雨雨”的数据流的防御处理过程。

      

      对于前端页面及js对数据流校验的处理,不妨看一下我的这两篇文章——谈谈代码健壮性之前端校验谈谈代码健壮性之极限值处理(防御性编程)

      对于后端(这里主要使用java)的处理,对于数据流的校验,我们多用断言(Assert)来进行校验处理。如——

    复制代码
    //字符串propertyName不可为空
    Assert.hasText(propertyName);
    
    //对象objectName不可为空
    Assert.notNull(objectName);
    
    //……
    复制代码

      我们当然也遇到赋予默认值的情况,如——

    String propertyName = (StringUtils.isNotEmpty(name) ? name : null);
    //......

      单单判空远远不够。举个例子,我们限制用户登录密码在6到16位且不可包含“,,.。%$”等特殊字符,具体的后端程序代码实现就不说了。只是想说明的是,可不要因为客户端做了校验,就忘记了在服务器端做校验。

      需要额外提的是,对于面向用户的应用来说,我们确实需要在前端及后端同时进行数据流校验,且校验规则是相同的。如果我们的校验规则仅仅去写一次,就能够同时作用于前端及后端,那就很方便去维护了。我们需要一个中间件去配置这些校验的规则。简单举个xml的配置方式,如——

    <!-- 这里的form的id对应表单的name属性值 -->
    <form id="loginForm">
        <!-- 这里的name对应页面表单的name属性值,required="true"表示该数据项是必填项,msg代表提示文本 -->
        <input name="username" required="true" length="1,30" msg="30个字符以内"></input>
        <input name="password" required="true" length="6,16" msg="6到16个字符以内"></input>
    </form>

      这样,不管是前端还是后端,我们可以通过解析这个xml文件来确保两者的校验规则是相同的。

      OK,到这里,后端的数据流处理介绍就到这里了,紧接着就介绍数据库端如何处理数据流了(这里我以mysql为例)。

      首先一点,便是插件数据时,对应的每一个数据项的默认值的处理。在创建表的语句中进行添加,如——

    CREATE TABLE t_xxx {
        property_name VARCHAR(255) not NULL DEFAULT '' COMMENT '字段注释',
        …
    }

      这样也就很大程度上避免了脏数据的存储。再举一个例子,我们在多表连接查询的时候,常常会碰到查询null项,这样我们就会用到查询赋予默认值这样类似的处理了,比如——

    SELECT t1.xxx, IFNULL(yyy,0)
    FROM t1 LEFT JOIN t2 
    ON t1.id = t2.zzz_id

      OK。数据库端的数据流校验处理介绍到这里。

      四、总结

      先引入这样一句话——

    软件滥用者形形色色,从利用程序小缺陷的不守规则的用户,到想尽办法非法进入他人系统的职业黑客。有太多的程序员在不经意间为这些人留下了可随意通过的后门。随着网络化计算机的兴起,粗心大意所带来的后果变得愈来愈显著了。

    许多大型软件开发公司终于意识到了这种威胁,开始认真思考这个问题,将时间和资源投入到严谨的防御性编码工作中。

      相信大家都能够体会到做好“防御性编程”的重要性。同时,我们需要知道,做好“防御性编程”,我们往往要多花出3倍的时间去处理(远远不单单是数据流的处理,如思考代码严谨性、思考信息提示、思考代码走向等等等等)。

      我想最重要的是,如果你想快速开发出高质量的模块,不仅需要经验的不断累积,培养数据流的防御性编程思维同样不可缺少。

  • 相关阅读:
    微服务实战——微服务架构选型SpringCloud / Dubbo / K8S比较(一)
    微服务实战——Spring Cloud + Zuul Gateway + Eureka集成
    微服务实战——SpringCloud与Feign集成
    微服务实战——高可用的SpringCloudConfig
    制作自己的网站第二步***在Linux上装上需要的软件以及部署项目配置**
    Eclipse打war包方法以及Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
    个人网站开发***云服务器+Linux+域名***
    SaaS 系统架构,Spring Boot 动态数据源实现!
    Spring Security 是如何在 Servlet 应用中执行的?
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3306059.html
Copyright © 2011-2022 走看看