最近在看 Jwt 相关内容时,发现对于解析拿取 claim , Jwt 有两种处理方式,一种是不被推荐的老方法:parser()( jjwt-api:0,10.x之前 ),另一种是新版本:parserBuilder()
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(refreshToken)
.getBody();
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(refreshToken)
.getBody();
从功能上看,parserBuilder提供了 setSigningKeyResolver ,支持动态密钥解析; setAllowedClockSkewSeconds 时钟偏移容差。
从设计上看,parser采用静态工厂,parserBuilder采用建造者模式而非静态工厂模式,具体过程如下:
graph TD
parser --> 创建配置对象 --> 使用对象
parser将创建与配置对象混合,结构上比较混乱,且可能存在配置后被修改的情况
graph TD
parserBuio --> 配置对象 --> build --> 创建不可变对象 -->使用对象
而parserBuilder则将配置与build分开,且建立起不可变对象好处如下:
- 获取线程安全
- build时可以添加校验
例子如下
// 在并发环境下
public class TokenValidator {
private JwtParser parser;
// 旧方式的问题:多线程可能同时修改配置
public TokenValidator() {
this.parser = Jwts.parser()
.setSigningKey("default");
}
public void validateTokensConcurrently(List<String> tokens) {
// 如果多个线程同时调用,可能互相干扰
tokens.parallelStream().forEach(token -> {
// 危险!可能另一个线程正在修改parser配置
parser.requireIssuer(getIssuerForToken(token));
parser.parseClaimsJws(token);
});
}
}
// 建造者可以在build()时进行配置验证
public JwtParser build() {
validateConfiguration(); // 验证所有必要配置
return new ImmutableJwtParser(config);
}
private void validateConfiguration() {
if (signingKey == null && signingKeyResolver == null) {
throw new IllegalStateException("必须设置签名密钥或密钥解析器");
}
}
现在分析会不会是因为开始时采用静态工厂,便于扩展,后发现可变问题,且敲定需求后转为建造者模式,为历史兼容仍旧保存。