Bootstrap

极客大学架构师训练营 组件设计原则 安全架构 防火墙ModSecurity 第21课 听课总结

说明

讲师:李智慧

组件设计原则

在没有编程语言的时候就已经有了软件组件。

软件的复杂度和它的规模成指数关系

一个复杂度为 100 的软件系统,如果能拆分成两个互不相关、同等规模的子系统,那么每个子系统的复杂度应该是25,而不是50.软件开发这个行业很久之前就形成了一个共识,应该将复杂的软件系统进行拆分,拆成多个更低复杂度的子系统,子系统还可以继续拆分成更小粒度的组件。也就是说,软件需要进行模块化、组件化设计。

组件内聚原则

组件内聚原则主要讨论哪些类应该聚合在同一个组件中,以便组件既能提供相对完整的功能,又不至于太过庞大。

  • 复用发布等同原则;

  • 共同封闭原则;

  • 共同复用原则。

复用发布等同原则

复用发布等同原则是说,软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果你希望别人以怎样的粒度复用你的软件,你就应该以怎样的粒度发布你的软件。这其实就是组件的定义了,组件是软件复用和发布的最小粒度软件单元。这个粒度既是复用的粒度,也是发布的粒度。

版本号约定建议:

  • 版本号格式:主版本号.次版本号.修订号。比如 1.3.12,在这个版本号中,主版本号是1, 次版本号是3, 修订号是 12.

  • 主版本号升级,表示组件发生了不向前兼容的重大修订;

  • 次版本号升级,表示组件进行了重要的功能修订或者 bug 修复,但是组件是向前兼容的;

  • 修订号升级,表示组件进行了不重要的功能修订或者 bug 修复。

共同封闭原则

共同封闭原则是说,我们应该将那些会同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同目的而修改的类放到不同的组件中。

组件的目的虽然是为了复用,然而开发中常常引发问题的,恰恰在于组件本身的可维护性。如果组件在自己的生命周期中必须经历各种变更,那么最好不要涉及其它组件,相关的变更都在同一个组件中。这样,当变更发生的时候,只需要重新发布这个组件就可以了,而不是一大堆组件都受到牵连。

共同复用原则

共同复用原则是说,不要强迫一个组件的用户依赖他们不需要的东西。

这个原则一方面是说,我们应该互相依赖,共同复用的类放在一个组件中。比如说,一个数据结构容器组件,提供数组、Hash 表等各种数据结构容器,那么对数据结构遍历的类、排序的类也应该放在这个组件中,以使这个组件中的类共同对外提供服务。

另一方面,这个原则也说明,如果不是被共同依赖的类,就不应该放在一个组件中。如果不被依赖的类发生变更,就会引起组件变更,进而引起使用组件的程序发生变更。这样就会导致组件的使用者产生不必要的困扰,甚至讨厌使用这样的组件,也造成了组件复用的困难。

组件耦合原则 (强原则,架构师必须要守住)

组件内聚原则讨论的是组件应该包含哪些功能和类,而组件耦合原则讨论组件之间的耦合关系应该如何设计。

  • 无循环依赖原则;

  • 稳定依赖原则;

  • 稳定抽象原则。

无循环依赖原则

无循环依赖原则说,组件依赖关系中不应该出现环。如果组件 A 依赖组件 B,组件 B 依赖组件 C,组件 C 又依赖组件 A,就形成了循环依赖。

很多时候,循环依赖是在组件的变更过程中逐渐形成的,组件 A 版本 1.0 依赖组件 B 版本 1.0,后来组件 B 升级到 1.1, 升级的某个功能依赖组件 A 的 1.0 版本,于是形成了循环依赖。如果组件设计的边界不清晰,组件开发设计缺乏评审,开发者只关注自己开发的组件,整个项目对组件依赖管理没有统一的规则,很有可能出现循环依赖。

稳定依赖原则

稳定依赖原则是说,组件依赖关系必须指向更稳定的方向。较少变更的组件是稳定的,也就是说,经常变更的组件是不稳定的。根据稳定依赖原则,不稳定的组件应该依赖稳定的组件,而不是反过来。

反过来说,如果一个组件被更多组件依赖,那么它需要相对是稳定的,因为想要变更一个被很多组件依赖的组件,本身就是一件困难的事。相对应的,如果一个组件依赖了很多的组件,那么它相对也是不稳定的,因为它依赖的任何组件变更,都可能导致自己的变更。

稳定依赖原则通俗地说就是,组件不应该依赖一个比自己还不稳定的组件。

稳定抽象原则

稳定抽象原则是说,一个组件的抽象化程度应该与其稳定性程度一致。也就是说,一个稳定的组件应该是抽象的,而不稳定的组件应该是具体的。

这个原则对具体开发的指导意义就是:如果你设计的组件是具体的、不稳定的,那么可以为这个组件对外提供服务的类设计一组接口,并把这组接口封装在一个专门的组件中,那么这个组件相对就比较抽象、稳定。

Java 中的 JDBC 就是这样一个例子,我们开发应用程序的时候只需要使用 JDBC 的接口编程就可以了。而发布应用的时候,我们制定具体的实现组件,可以是 MySQL 实现的 JDBC 组件,也可以是 Oracle 实现的 JDBC 组件。

组件的边界与依赖关系,不仅仅是技术问题

组件的边界与依赖关系划分,不仅需要考虑技术问题,也要考虑业务场景问题。易变与稳定,依赖与被依赖,都需要放在业务场景中去考察。有的时候,甚至不只是技术业务的问题,还需要考虑人的问题,在一个复杂的组织中,组件的依赖与设计需要考虑人的因素。如果组件的功能划分涉及部门的职责边界,甚至会和公司内的政治关联起来。

安全架构

XSS Cross Site Scripting 跨站脚本攻击

新浪微博曾经遇到的攻击。一个用户发布微博,TA的好友看到了,转发微博后,用户的好友也发起了攻击。

查看微博的时候,从服务器获取到恶意脚本,在浏览器上会再次执行。

解决方案:用户不能发脚本文件给服务器。

XSS 攻击防御手段

消毒:XSS 攻击装一般都是通过在请求中嵌入恶意脚本达到攻击目的,这些脚本是一般用户输入中不适用的,如果进行过滤和消毒处理,即对这个某些 HTML 危险字符转义,如“>” 转义为 “>”、“”<转义为 “<” 等,就可以防止大部分攻击。为了避免对不必要的内容错误转义,如 “3<5” 中的 “<”,需要进行文本匹配后再转义。如 “

SQL 注入攻击

效果:删除users表。

注入攻击防御手段1

消毒:和防 XSS 攻击一样,请求参数消毒是一种比较简单粗暴又有效的手段。通过正则匹配,过滤请求数据中可能注入的 SQL 文。

如 “”、“” 等。

注入攻击防御手段2

SQL 预编译参数绑定:使用预编译手段,绑定参数是最好的防 SQL 注入方法。目前许多的数据访问层框架,如 MyBatis,Hibernate 等,都实现 SQL 预编译和参数绑定,攻击者的恶意 SQL 会被当做 SQL 的参数,而不是 SQL 命令被执行。

获取数据库表结构信息的手段

  • 开源:如果网站采用开源软件搭建,如用 Discuz!搭建论坛网站,那么网站数据库结构就是公开的,攻击者可以直接获得。

  • 错误回显:如果网站开启错误回显,攻击者故意构造非法参数,服务端异常信息会输出到浏览器端,为攻击猜测数据库表结构提供了便利。

  • 盲注:网站关闭错误回显,攻击者根据页面变化情况判断 SQL 语句的执行情况,据此猜测数据库结构,此种方式攻击难度较大。

CSRF Cross Site Request Forgery 跨站点请求伪造 攻击

利用登录过的用户信息,伪造用户请求,一般通过302跳转去攻击服务器。

CSRF 攻击防御手段

其它需要关注的攻击和漏掉

Web 应用防火墙

开源 Web 应用防火墙 ModSecurity

ModSecurity 是一个开源的 Web 应用防火墙,探测攻击并保护 Web 应用程序,既可以嵌入到 Web 应用服务器中,也可以作为一个独立的应用程序启动。 ModSecurity 最早只是 Apache 的一个模块,现在已经有 Java、.NET 多个版本,并支持 Nginx。

ModSecurity 采用处理逻辑与规则集合分离的架构模式。处理逻辑负责请求和响应的拦截过滤,规则加载执行等功能。而规则集合则负责对具体的攻击的规则定义、规模识别、防御策略等功能。处理逻辑比较稳定,规则集合需要不断针对漏洞进行升级,这是一种可扩展的架构设计。

网站安全漏洞扫描

和电脑安全漏洞扫描一样,网站也需要安全漏洞扫描。

网站安全漏洞扫描工具是根据内置规则,模拟黑客攻击行为,用以发现网站安全漏洞的工具。许多大型网站的安全团队都有自己开发的漏洞扫描工具,不定期的对网站的服务器进行扫描,查漏补缺。

目前市场上也有许多商用的网站安全漏洞扫描平台。

总结

如果出现循环依赖,系统就挂了。

架构师没有写架构文档,就像程序员不写代码。