记一次低级错误排查经历(Long和long到底怎么用)

最近打开之前做过的一个项目,看了看其中的源代码。在我接手后续开发之前,项目中存在一个低级但又不那么容易察觉的错误。我觉得这个错误不只当时的开发人员会犯,而且很多开发人员也会犯的一个错误。

让我们一起来看看是怎么回事。

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

猜你喜欢

转载自juejin.im/post/5e646bd1e51d450edd2926b6