目录
一、编码规范
接口规范
命名规范
并发处理
常量定义
集合处理
控制语句
事务规范
其他规范
二、异常日志
日志规范
异常规范
三、工程规约
服务器规约
二方库规约
四、MySQL数据库规范
建表规范
索引规范
SQL语句
五、安全规约
XT开发规范
一、编码规范
接口规范
1、RPC‘接口返回值’以及‘接口入参值’,禁止使用枚举;枚举在你的系统内部使用即可。推荐在这个字段上注释一个枚举类型。【强制】
正例:
/** * 类型 {@link com.atta.infra.workorder.api.constrants.WorkOrderType} */
private String type;
2、非查询接口,必须是幂等的。【强制】(幂等定义说明)(幂等返回值方式:告知单号、告知重复,都是成功)幂等ID由上游提供,保证不同业务不同幂等ID。
不考虑并发情况,update本来技术幂等的。根据业务场景,如果是对status的修改,那么修改之前是需要对数据库status进行判断校验的。
3、大数据量查询必须提供分页操作,每页size不超过200条(dba强制:in语句在100条以内)。【强制】(框架解决)
4、浮点数(金额)必须使用BigDecimal,必须使用String构造方法。【强制】(增加案例)
说明:
1)防止精度丢失
5、日元、韩元、台币不允许有小数点。【强制】(补充其他币种:越南币...)
说明:
1)日元、韩元、台币最小单位是元,人民币、美元最小单位是分。
2)日元、韩元、台币,银行系统不支持小数点,会报错。
6、webapp接口,必须做水平权限检查。【强制】
说明:
1)例如:接口是根据id查询业务信息,必须校验id是当前用户的id,否则会出现水平权限漏洞。
正例:
@GetMapping("bankcard/detail")
public bankcardModel getBankcardDetail(@RequestParam String bankcardId){
BankcardVO bankcardVO = bankcardClient.queryBankcardProfile(bankcardId);
if(!UserUtils.getFirmId().equals(bankcardVO.getFirmId()){
//throw 403 异常
}
if(UserUtils.getFirmId().equals(bankcardVO.getFirmId())
return convertVO2Model(bankcardVO);
}
反例:
@GetMapping("bankcard/detail")
public bankcardModel getBankcardDetail(@RequestParam String bankcardId){
BankcardVO bankcardVO = bankcardClient.queryBankcardProfile(bankcardId);
return convertVO2Model(bankcardVO);
}
7、敏感数据(手机号、邮箱、企业名称、身份证号、地址等)必须脱敏返回 【强制】(增加安全规范连接)日志里面不能打印用户的信息。打印id即可,然后自己去数据库里面查。手机号、邮箱这种定位到人的,属于敏感;你可以加*来脱敏打印。
说明:可以使用脱敏注解。
8、增删改查,可以提供批量接口的,尽量提供批量接口(批量的size也得有限制)。【推荐】
9、禁止在接口实现中修改接口的入参。【强制】
命名规范
1、数据库表名,必须加数据库缩写前缀。【强制】
说明:可以避免数仓同步数据,表重名问题。
正例:ul_audit_request boss_audit_request 反例:audit_request
2、【强制】抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以 它要测试的类的名称开始,以Test结尾。
3、【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例:应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考spring的框架结构)
4、【推荐】子程序的名字尽可能的表达出所实现的功能,建议是单子程序只实现单功能(单一原则); 而接口的名字需要站在更高的位置,表达出高度的抽象,其名称只需要表达出主业务功能即可。
-
子程序实现单一功能时,其名称需要具体到所实现的某种功能:如果子程序只是参数组装(填充):子程序名字可以是assem、fill甚至modify这些动词开头;
-
子程序实现多个功能时/定义接口时,其名称需要抽象表达主业务功能:如果子程序/接口既需要调用api接口,又需要插入数据库,又需要调用第三方接口:子程序名字可以是handle、process等动词开头,以表达更高的抽象。
5、【参考】各层命名规约:
-
Service/DAO
层方法命名规约
- 获取单个对象的方法用
get
做前缀。
- 获取多个对象的方法用
list
做前缀。
- 获取统计值的方法用
count
做前缀。
- 插入的方法用
save
(推荐)或 insert
做前缀。
- 删除的方法用
remove
(推荐)或 delete
做前缀。
- 修改的方法用
update
做前缀。
- 领域模型命名规约
- 数据对象:
xxxDO
,xxx
即为数据表名。与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
- 数据传输对象:
xxxDTO
,xxx
为业务领域相关的名称。数据传输对象,Service 和 Manager 向外传输的对象。
- 展示对象:
xxxVO
,xxx
一般为网页名称。显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
- BO:业务对象。可以由 Service 层输出的封装业务逻辑的对象。
-
POJO
是 DO/DTO/BO/VO
的统称,禁止命名成 xxxPOJO
。
并发处理
1、线程池队列如果使用LinkedBlockingQueue,必须设置队列大小。【强制】
2、数据库单条更新操作,必须使用version做乐观锁,modified_time也必须更新;批量更新用status做乐观锁,modified_time也必须更新。【强制】
3、日期格式化必须使用XT自己的DateFormatter工具类。SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁。【强制】
4、【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的 处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
5、【强制】创建线程或线程池时请指定有意义的线程名称,方便监控和出错时回溯。
正例:
public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName("TimerTaskThread"); ...
}
常量定义
1、禁止用内部类初始化对象。【强制】
反例:
user.setAddress(new Address(){{
setStreet("荆州路");
setCity("上海");
setProvince("上海");
}})
2、【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如: 缓存相关的常量放在类: CacheConsts 下;系统配置相关的常量放在类: ConfigConsts 下。
说明 : 大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
集合处理
1、不允许for循环查询数据库,使用批量查询。【强制】
2、【强制】关于hashCode和equals的处理,遵循如下规则:
- 只要重写equals,就必须重写hashCode。
- 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
- 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
3、【推荐】List 优于数组, 优先使用 list 而不是数组
4、【推荐】要多使用 Collections 集合工具类
-
使用空集合是使用 Collections.emptyList() Collections.emptyMap()
-
当集合里面只有一个参数的时候,使用 Collections.singletonList(); Collections.singletonMap();
-
使用静态集合变量,使用 Collections.unmodifiableList(), Collections.unmodifiableMap()
控制语句
1、在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。【强制】
2、复杂表达式,要抽象为一个方法,并增加注释。【推荐】
-
复杂表达式:5个以上。
-
不允许超过20个,超过说明设计方案有问题。
3、推荐尽量少用else, if-else的方式可以改写成:【推荐】
if(condition){
...
return obj;
}
//接着写else的业务逻辑代码;
说明:如果非得使用if()...else if()...else...方式表达逻辑,请勿超过 3层,超过请使用状态设计模式。
事务规范
1、本地事务一律使用编程式事务。 【强制】声明式事务不容易引起开发人员的关注;
说明:
1.注解事务只能应用到public可见度的方法上,如果应用在protected、private可见度的方法上事务设置不起作用,且不会报错
2.注解事务对checked异常(IOException、TimeoutException)不会回滚
3.同一类中的方法调用事务不会生效
2、update方法要检查返回值是否是期望的,也必须放在DAO层检查。【强制】
3、如果事务内catch异常处理,必须手动回滚。【强制】
//反例
transactionTemplate.execute(status -> {
try {
...
} catch (Exception e) {
log.error(...);
return ...;
}
};
//正例
transactionTemplate.execute(status -> {
try {
...
} catch (Exception e) {
status.setRollbackOnly();
log.error(...);
return ...;
}
}
4、我们的mysql数据库事务隔离级别设置为read-commited。不要在一个事务里面重复读。【参考】
5、我们目前的事务传播是REQUIRES_NEW,新开启事务,挂起老事务,因此要注意嵌套事务的问题,父事务rollback,子事务不会rollback。【参考】
其他规范
1、禁止使用BeanUtils.copyProperties(A,B)或其他类似工具,手写转换方法就好【强制】
2、定时任务的job接口,必须是异步执行。【强制】
二、异常日志
日志规范
1、日志中禁止打印客户的敏感信息(密码、秘钥、手机号、邮箱、身份证号、企业名称、住址等)。【强制】
- 推荐:不要打印完整对象
- 推荐:按需打印对象中的信息,如:id。
2、禁止使用e.printStackTrace()。【强制】
说明:直接这么用,线上异常栈打不到日志中。
3、system.out.print禁止使用(增)【强制】
4、【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上抛。
正例:logger.error(各类参数toString + "_" + e.getMessage(), e);
5、【强制】对trace/debug/info级别的日志输出,必须使用条件输出形式或者使用占位符的方 式。
说明:logger.debug(“Processing trade with id: “ + id + “ symbol: “ + symbol);如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。
正例:(条件)
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);
6、日志一定要加入关键输入/输出参数,不要打印大量无关信息。【推荐】
打log导致系统异常故障
异常规范
1、【强制】对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代 码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。
2、【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事 务。
3、【强制】不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块、catch块中的return语句。
4、【强制】方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什 么情况下会返回null值。调用方需要进行null判断防止NPE问题。 说明:本规约明确防止NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回null的情况。
三、工程规约
服务器规约
-
【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 说明: 操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接,在高并发访问下,服 务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上 调小此等待值。
正例 : 在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值(秒): net.ipv4.tcp_fin_timeout = 30
-
【推荐】调大服务器所支持的最大文件句柄数(File Descriptor,简写为 fd)。
说明 : 主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对 应于一个 fd。主流的 linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
-
【推荐】给 JVM 设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。
说明 : OOM 的发生是有概率的,甚至有规律地相隔数月才出现一例,出现时的现场信息对查错 非常有价值。
-
【参考】服务器内部重定向使用 forward;外部重定向地址使用 URL 拼装工具类来生成,否则 会带来 URL 维护不一致的问题和潜在的安全风险。
-
webapp服务,禁止连接数据库。【强制】
-
webapp连接的redis和server连接的redis不能用相同的key。(架构上处理)【强制】。 说明:redis的key只能在一个服务中出现,如果另一个服务需要访问同一个key的redis数据,通过接口提供访问。
二方库规约
- 【强制】定义 GAV 遵从以下规则:
- GroupID 格式: com.{公司/BU }.业务线.[子业务线],最多 4 级。
说明 : {公司/BU} 例如: alibaba/taobao/tmall/aliexpress 等 BU 一级;子业务线可选。
正例 : com.taobao.jstorm 或 com.alibaba.dubbo.register
- ArtifactID 格式: 产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
正例 : dubbo-client / fastjson-api / jstorm-tool
- Version: 详细规定参考下方。
- 【强制】二方库版本号命名方式: 主版本号.次版本号.修订号
- 主版本号: 当做了不兼容的 API 修改,或者增加了能改变产品方向的新功能。
- 次版本号: 当做了向下兼容的功能性新增(新增类、接口等)。
- 修订号: 修复 bug,没有修改方法签名的功能加强,保持 API 兼容性。
说明 : 注意: 起始版本号必须为: 1.0.0,而不是 0.0.1 正式发布的类库必须先去中央仓库进行查证,使版本号有延续性,正式版本号不允许覆盖升级。如当前版本: 1.3.3,那么下一个合理的版本号: 1.3.4 或 1.4.0 或 2.0.0
-
【强制】线上应用不要依赖 SNAPSHOT 版本(安全包除外)。
说明 : 不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。
-
【强制】二方库的新增或升级,保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变, 必须明确评估和验证,建议进行 dependency:resolve 前后信息比对,如果仲裁结果完全不一 致,那么通过 dependency:tree 命令,找出差异点,进行<excludes>
排除 jar 包。
-
【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。
-
【强制】依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。 说明: 依赖 springframework-core,-context,-beans,它们都是同一个版本,可以定义一 个变量来保存版本: ${spring.version},定义依赖的时候,引用该版本。
-
【强制】禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的 Version。
说明 : 在本地调试时会使用各子项目指定的版本号,但是合并成一个 war,只能有一个版本号 出现在最后的 lib 目录中。可能出现线下调试是正确的,发布到线上却出故障的问题。
-
【推荐】所有 pom 文件中的依赖声明放在<dependencies>
语句块中,所有版本仲裁放在 <dependencyManagement>
语句块中。
-
<dependencyManagement>
里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖,而version 和 scope 都读取自父 pom。如果子项目自己也声明了version、scope,那么就用子项目自己的。
-
<dependencies>
所有声明在主 pom 的 <dependencies>
里的依赖都会自动引入,并默认被所有的子项目继承。
-
声明:同一个pom文件可以同时具备 <dependencyManagement>和<dependencies>。 该pom文件可以自娱自乐,自己在<dependencyManagement>里面定义一个依赖 并指定版本,然后在<dependencies>中
声明这个依赖。参考链接
-
【推荐】二方库不要有配置项,最低限度不要再增加配置项。
-
【参考】为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则:
1)精简可控原则。移除一切不必要的 API 和依赖,只包含 Service API、必要的领域模型对 象、Utils 类、常量、枚举等。如果依赖其它二方库,尽量是 provided 引入,让二方库使用 者去依赖具体版本号;无 log 具体实现,只依赖日志框架。
2)稳定可追溯原则。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能 方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。
-
引入3方包,需要向基础架构组申请,版本号放到atta-infra项目中。【强制】
-
【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的POJO对象。
四、MySQL数据库规范
建表规范
-
建表统一增加创建时间(created_time)、修改时间(modified_time)、版本号(version), 并且都为非空字段。 【强制】
-
必填字段都需要定义成非空【强制】
-
约定表名和所有的字段名全部为小写,表名和字段名全部为英文,并带有具体的含义,建表语句的comment需要填写, 要描述清楚表和字段的含义, comment可以为中文。【强制】
-
【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
-
【强制】表名、字段名、索引名应由小写字母、数字和下划线组成,且以小写字母开头。禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例 : getter_admin,task_config,level3_name
反例 : GetterAdmin,taskConfig,level_3_name
-
表名、字段名应采用能表达业务含义的抽象名词,禁止出现渠道名、合作商等具体名词【强制】
-
表名不使用复数名词【强制】
-
表名、字段名不能使用Mysql的保留字,如 desc、range、match、delayed 等。【强制】
-
【强制】表达是和否概念的字段,命名规则为:is_xxx。数据类型是 unsigned tinyint( 1 表示是,0 表示否)。
说明 : 任何字段如果为非负数,必须是 unsigned。
正例 : 表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
-
主键索引的命名规则为:pk_xxx;唯一索引的命名规则:uk_xxx;普通索引的命名规则为:idx_xxx【强制】
-
表的必备字段:id、created_time、modified_time、version【强制】
-
【强制】推荐表设计时冗余适当的字段以提高查询性能。字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1)不是频繁修改的字段。
2)不是 varchar 超长字段,更不能是 text 字段。
正例 : 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。
-
【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明 : 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
-
-
小数类型采用decimal,禁止使用float或double。【强制】
-
非负数字段,采用unsigned,避免存入负数【强制】
- DAO方法参数,必须做判空检查或用Lombok的@NonNull,禁止传入空值。【强制】
- DO的成员变量,不要用基础类型int,boolean等,基础类型的默认值会导致数据被修改。【推荐】
-
不要去写一个大而全的数据更新方法,然后传入POJO类,这样非常容易出错,而且更新无谓的字段,加大消耗。【强制】
索引规范
-
【强制】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。 说明: 不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
-
【推荐】如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例 : where a=? and b=? order by c; 索引: a_b_c
反例 : 索引中有范围查找,那么索引有序性无法利用,如: WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
-
【推荐】利用覆盖索引来进行查询操作,避免回表。
说明 : 如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗? 目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例 : 能够建立索引的种类: 主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种 效果,用 explain 的结果,extra 列会出现: using index。
-
【推荐】利用延迟关联或者子查询优化超多分页场景。
说明 : MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例 : 先快速定位需要获取的 id 段,然后再关联: SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
-
【推荐】SQL 性能优化的目标: 至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
说明 :
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例 : explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range 还低,与全表扫描是小巫见大巫。
-
【推荐】建组合索引的时候,区分度最高的在最左边。
正例 : 如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明 : 存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如: where a>? and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。
-
【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
-
【参考】创建索引时避免有如下极端误解:
1)宁滥勿缺。误认为一个查询就需要建一个索引。
2)宁缺勿滥。误认为索引会消耗空间、严重拖慢更新和新增速度。
3)抵制惟一索引。误认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。
-
限制每张表上的索引数量,建议单张表索引不超过5个【推荐】
-
索引长度不能超过767,注意计算联合索引的长度【强制】
-
在varchar字段上建索引时,必须指定索引长度【强制】
-
常用查询的条件要有索引, 注意顺序是否正确【强制】
-
删除不再使用或者很少使用的索引【强制】
-
最左前缀匹配原则,非常重要的原则【强制】
- 比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可【强制】
- 索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引【强制】
SQL语句
-
【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明 : count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
-
【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
-
【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。
正例 : 可以使用如下方式来避免 sum 的 NPE 问题: SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
-
【强制】数据订正时,删除和修改记录时,要先 select,避免出现误删除,确认无误才能执行更新语句。
-
【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
-
禁止物理删除,用逻辑删除。0为非删除态。【强制】
-
禁止代码中使用select * from进行查询,给出明确的待查询字段。【强制】
-
update操作的where字段要有index。【强制】
-
严禁没有where的update操作。【强制】
-
alter 表必须支持提前执行。【强制】
-
重试job必须增加时间间隔,避免并发,如:查询5分钟前的数据。【强制】
-
使用#设置参数,like、正则参数判断,必须进行转义。【强制】
-
禁止join操作。【强制】
五、安全规约
-
【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。
说明 : 防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信 内容、修改他人的订单。
-
【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
说明 : 查看个人手机号码会显示成:158****9119,隐藏中间 4 位,防止隐私泄露。
-
【强制】用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库。
-
【强制】用户请求传入的任何参数必须做有效性验证。
说明 : 忽略参数校验可能导致:
- page size 过大导致内存溢出
- 恶意 order by 导致数据库慢查询
- 任意重定向
- SQL 注入
- 反序列化注入
- 正则输入源串拒绝服务 ReDoS
说明 : Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题, 但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。
-
【强制】禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。
-
【强制】表单、AJAX 提交必须执行 CSRF 安全过滤。
说明 : CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在 CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户 不知情情况下对数据库中用户参数进行相应修改。
-
【强制】在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制, 如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。 说明: 如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其 它用户,并造成短信平台资源浪费。
-
【推荐】发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过 滤等风控策略。