背景

某项目OIM资源历史记录时间显示时区不对,比北京时间少了8个小时

正常时间:

导致了跨时区用户无法登录,账户被锁,在修改了所有跟时区有关的环境信息后(包括操作系统时区,数据库时区,jvm时区,oim时区)依然无法解决。

排查

OIM本身就有自定义界面的功能,OIM管理界面由oracle adf开发,时间显示问题不止出现历史记录这里,其他界面也有同样的问题,但都可以通过更改adf taskflow解决,这个界面比较特殊就在于它是通过iframe嵌入,使用的技术栈也并不是adf,所以无法自定义。因为是iframe嵌入所以可以单独把url拿出来便于调试

http://10.10.10.10:14000/OIGUI/index.html?moduleName=reshistroy&operationName=reshist&locale=zh-CN&processInsatnceKey=172430&action=resourceHistory

界面会调用http://10.10.10.10:14000/iam/governance/selfservice/api/v1/provprocesses/172430获取数据,接口返回数据如下:

可以看到接口本身返回就是错误的时间,所以问题肯定是出在后端接口上,好在这个界面前后端分离,排查起来相对简单点。

我们可以根据接口路径/iam/governance/selfservice/api/v1/provprocesses/172430很容易在console的deployment找到具体应用oim,如果你不知道如何根据url在weblogic上查找应用程序,可以阅读这篇博客了解下。我们可以在Testing界面找到对应的url

在Overview界面找到应用程序位置(空格要去掉)

找到代码位于Middleware/Oracle_Home/idm/server/apps/oim.ear/APP-INF/lib/rest-oig-service.jar中,接口代码位于oracle.iam.rest.self.services.ProvisionProcessesImplgetProvProcessByProcessId方法内,整理出来调用顺序如下:

  • 1. oracle.iam.rest.self.services.ProvisionProcessesImpl.getProvProcessByProcessId
  • 2. oracle.iam.rest.adapter.ProvisionProcessesConverterImpl.getProvProcessByProcessId
  • 3. oracle.iam.rest.adapter.ProvisionProcessesConverterImpl.translatedNoteToDisplay
  • 4. oracle.iam.rest.commons.utils.DateUtils.formatUTC

真正实现日期格式化代码位于oracle.iam.rest.commons.utils.DateUtils.formatUTC中,代码如下:

public static String formatUTC(java.util.Date date, Locale locale, String format){
  if (date == null) {
    return null;
  }
  SimpleDateFormat sdf = dateFormatWithLocaleTimezone(locale, "GMT", format);
  
  return sdf.format(date);
}

代码逻辑较简单,注意到调用了一个函数dateFormatWithLocaleTimezone(locale, "GMT", format),并且第二个参数是字符串常量GMT

private static SimpleDateFormat dateFormatWithLocaleTimezone(Locale locale, String timeZone, String format){
  String dateFormat = getDateFormat(format, locale);
  
  SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, locale);
  sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
  
  return sdf;
}

从代码可以看出,接口对日期进行了格式化,并且代码写死了根据GMT时区进行格式化,因此无论外部环境怎么修改,这里时间都是按照GMT时区进行格式化,这个应该是oracle众多bug中的一个小bug。为了验证我们的猜想,我们用神器arthas对OIM进行调试。启动arthas后,使用watch命令查看调用情况

watch oracle.iam.rest.commons.utils.DateUtils formatUTC "{params[0],params[1],params[2],returnObj}" -x 2

确实经过处理后时间少了8小时。

解决

既然是代码中硬编码写死,那要怎么解决,有两种思路

  • 给oracle提交bug,oracle确认后在会在下个补丁中进行修复
  • 自己通过一些办法对代码进行修改

如果是走官方渠道,需要较长的时间,并且需要准备足够的资料,如果有条件建议使用该方案,因为我们没有源码,所以如果想自行修复需要对代码进行反编译修改后再重新编译,这其中有以下问题:

  • 重新编译使用的jdk需要和编译原代码jdk保持一直
  • 需要解决编译依赖问题
  • 对于一些语法糖反编译后的代码是不包含的

重新编译是存在风险的,有可能造成未知问题,不在万不得已的情况下,非常不建议这种方案,但如果只是修改class文件中的字符常量而不修改代码,根据修改后的字符串长度和原字符串长度是否一样可以有两种方案

  • 如果长度一样,直接通过类似ultraedit十六进制编辑器进行修改即可
  • 如果长度不一样,需要借助专门的工具进行修改,你可以去搜索修改class文件字符串的工具,还是有一些,比如mcntools这个工具,本来是用来给游戏做汉化,里面包含了class文件字符串修改功能。

本次需要把class中的字符串GMT改为Asia/Shanghai,长度不一样,借助了工具mcntools进行修改

修改完后,在rest-oig-service.jar目录下创建目录oracle/iam/rest/commons/utils/,把DateUtils.class文件放到utils文件夹下,在rest-oig-service.jar目录下执行以下命令更新jar即可

jar -uvf rest-oig-service.jar oracle/iam/rest/commons/utils/DateUtils.class
一定要提前备份好rest-oig-service.jar文件

重启服务器,问题解决

Trackback

no comment untill now

Add your comment now