问题背景
- 业务中涉及参数的加签,验签,接口调用方需要将业务参数,过期时间,以及其他扩展参数一并生成签名发送给接口提供方,再由其解密验签
- 业务上线后运行中,会在某些时段抛出验签失败的异常
问题排查
- 由异常抛出的时间,初步推测为定时脚本引起,因为时段大部分与脚本运行时间重合,但在后续排查中发现同样的异常还出现在其他业务流程中,故并非是由脚本逻辑引起的
- 根据验签失败,推测加签时出现问题,开始排查加签的代码,由于同一时段会爆出大量异常,推测可能是由于该时段的正常业务叠加了脚本中的大量请求,导致的并发问题
- 分析加签的代码,由于业务参数变动属于正常范围,初步怀疑是过期时间生成问题,在本地使用5000个线程进行并发测试,并未发现过期时间的并发异常
- 继续排查代码发现,过期时间固定设置为1分钟,并使用Calendar.roll()方法进行时间加减,通过日志精确时间排查,发现所有的异常,都固定在每小时的59分左右,故在本地将Calendar时间设置为59分进行调试
- 最终发现roll的算法与add并不一样,例如当前时间16:59:00,roll(Calendar.MINTUES,1),会将时间设置为16:00:00,而add方法会将时间设置为17:00:00,此时使用roll加减时间后进行加签,则会出现过期时间异常的情况
问题处理
- 已隐去业务相关部分
- 其中注释的行为产生问题的代码,使用LocalDateTime代替Calendar,亦可使用Calendar.add,此处不做讨论
private String createToken(PrivateKey privateKey, String requestBodyHash) {
Map<String, Object> claims = new HashMap<>();
claims.put("rbh", requestBodyHash);
// TimeZone utc = TimeZone.getTimeZone("UTC");
// Calendar expires = Calendar.getInstance(utc);
// expires.roll(Calendar.MINUTE, 1);
LocalDateTime localDateTime = LocalDateTime.now();
localDateTime = localDateTime.plus(1, ChronoUnit.MINUTES);
final Date date = Date.from(localDateTime.atZone(ZoneId.of("+0")).toInstant());
return Jwts.builder()
.setClaims(claims)
.setIssuer("")
.setAudience("")
.setExpiration(date)
.setSubject("")
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。