最近打开之前做过的一个项目,看了看其中的源代码。在我接手后续开发之前,项目中存在一个低级但又不那么容易察觉的错误。我觉得这个错误不只当时的开发人员会犯,而且很多开发人员也会犯的一个错误。
让我们一起来看看是怎么回事。
1、背景
一般,项目中在对数据操作之前,都需要进行授权验证,检测当前用户是否有足够权限进行相应的操作。
该项目中有一个对Schedule实体类进行操作的服务类ScheduleService。
ScheduleService类在对Schedule进行更新和删除的操作之前要对权限检测,具体代码如下所示:
if (SecurityUtils.getCurrentUser().getRole() != Role.SYSTEM_ADMIN
&& dbSchedule.getCompanyId() != SecurityUtils.getCurrentUser().getCompanyId()) {
throw new AccessDeniedException(
"你不允许修改不属于你公司的计划。");
}
if (SecurityUtils.getCurrentUser().getRole() == Role.OPERATOR
&& dbSchedule.getUserId() != SecurityUtils.getCurrentUser().getId()) {
throw new AccessDeniedException(
"你不允许修改不属于你的计划。");
}
- getCompanyId()返回公司id,为Long类型;
- dbSchedule.getUserId()返回Schedule的创建用户id,为Long类型;
- getCurrentUser().getId()返回当前登陆用户id,为Long类型;
- Role.SYSTEM_ADMIN代表系统管理员角色;
Role.OPERATOR表示操作员;还有一个Role.OPERATOR_ADMIN为操作管理员。 - 具体权限分配如下:
系统管理员可以修改删除任意Schedule;操作管理员可以修改删除所在公司的Schedule;操作员只允许修改删除自己创建的Schedule。
数据库使用的SQLServer,其数据表中,定义公司表中companyId(对应公司id)和用户表中userId(对应用户id)字段都为bigint类型。
同时,测试数据中,companyId和userId分别从1开始。companyId为1、2的分别代表公司C1、C2;
userId为5的为系统管理员;userId为1、2代表公司C1的操作管理员、操作员;userId为3、4代表公司C2的操作管理员、操作员。
权限分配表如下:
用户名(username) | 用户id(userId) | 所属公司名(companyName) | 所属公司id(companyId) | 更新Schedule的权限 |
---|---|---|---|---|
sysadmin | 5 | 所有Schedule | ||
operatoradmin1 | 1 | C1 | 1 | companyId=1的Schedule |
operator1 | 2 | C1 | 1 | companyId=1并且schedule.userId=2的Schedule |
operatoradmin2 | 3 | C2 | 2 | companyId=2的Schedule |
operator2 | 4 | C2 | 2 | companyId=2并且schedule.userId=4的Schedule |
测试过程中,程序一切运行正常,权限检测也正常。
然而在QA阶段,我们用客户提供的数据测试的时候,问题来了。
- username="operatoradmin1"的操作管理员不能更新companyName="C1"的Schedule;
- username="operatoradmin2"的操作管理员不能更新companyName="C2"的Schedule;
- username="operator1"的操作管理员不能更新companyName="C1"且自己创建的Schedule;
- username="operator2"的操作管理员不能更新companyName="C2"且自己创建的Schedule;
这就让人恼火啦!!!
2、排查
问题出现了,必须的找问题啊。。。不断追查代码,结果还是没能发现问题所在。
于是开始利用调试,在所有涉及权限检测的行设置断点,结果我们在运行到上述代码段时,发现
dbSchedule.getCompanyId()为1001时,居然不等于SecurityUtils.getCurrentUser().getCompanyId()为1001。
而这里应该是相等的!!!
问题肯定是出在这里啦!我们忽略了companyId和userId都是Long,而不是long。
但是为什么测试的时候没有一点问题呢?
我们发现客户提供的真实数据中,companyId和userId分别从1001开始。而测试数据中,companyId和userId分别从1开始。这也是导致问题被掩盖的原因之一。
事实上,这个问题超级低级,但是由于测试时并没有问题,所有问题掩盖在大量代码之中。
3、解决
解决这个问题有3种办法:
- 将dbSchedule.getCompanyId()改为dbSchedule.getCompanyId().longValue()以及
dbSchedule.getUserId()改为dbSchedule.getUserId().longValue(); - 将dbSchedule.getCompanyId()改为+dbSchedule.getCompanyId()以及
dbSchedule.getUserId()改为+dbSchedule.getUserId();
这2种办法实际都是将Long转换为long,然后进行原始类型long的相等比较“=”;
第2种方法要注意,+运算将Long转换为long的条件是+后面的语句不能返回null。
- 调用equals方法而不是使用==操作符来实现Long对象的比较。
4、总结
- 能够使用原始数据类型long或者int的时候尽量使用原始数据类型;
- 迫不得已需要使用包裹类Long或者Integer的时候:
- 一定要使用equals方法比较相等性;
- 通过longValue或者intValue转换为原始类型进行相等性比较。
- +运算将Long转换为long或者将Integer转换为int的时候,要注意前提条件是+后面的语句不能返回null。
觉得好?关注公众号:技术之禅(微信号"zen_of_java")获取更多干货和资源!
公众号回复"java","spring","javascript","python","english"等获取福利。