问题描述
自从第二章将认证服务搭建起来以后就很少更改基础配置了,直到今天有位读者提了一个问题,对我发出了灵魂拷问:重启后jwks的配置发生了变化,无法正确解析token,那Auth服务重启后有办法规避这种情况吗?假如生产环境这样,服务重新部署后之前所有登录的用户必须重新登陆。原问题见下方附图
解决方案分析
当时本人想到的是将生成的JWKSource
保存至redis,每次重启从redis中获取,这样不管重不重启生成的都是同一个,也就不会出现服务重启后无法解析在有效期内的AccessToken问题了,但是又怕不能序列化,就去查看代码了,最终功夫不负有心人,经过一番查找后发现最主要的配置是来自JWKSet
的实例,在JWKSet
找到了toString
方法和对应的parse方法
,toString方法是将jwks的信息转为json字符串,而parse就是读取并解析json字符串,将其转为JWKSet
实例
JWKSet中的toString方法
方法内部将jwks转为一个json字符串返回
JWKSet中的parse方法
方法内部根据给定的字符串解析并返回一个JWKSet。
编码解决问题
根据上边的分析得出解决方案,在配置JWKSource之前先从redis中尝试获取一下,获取不到就生成并存入redis;获取到直接解析并生成一个JWKSet;修改AuthorizationConfig
类中的JWKSource配置,如下所示
/**
* 配置jwk源,使用非对称加密,公开用于检索匹配指定选择器的JWK的方法
*
* @return JWKSource
*/
@Bean
@SneakyThrows
public JWKSource<SecurityContext> jwkSource() {
// 先从redis获取
String jwkSetCache = redisOperator.get(RedisConstants.AUTHORIZATION_JWS_PREFIX_KEY);
if (ObjectUtils.isEmpty(jwkSetCache)) {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// 生成jws
JWKSet jwkSet = new JWKSet(rsaKey);
// 转为json字符串
String jwkSetString = jwkSet.toString(Boolean.FALSE);
// 存入redis
redisOperator.set(RedisConstants.AUTHORIZATION_JWS_PREFIX_KEY, jwkSetString);
return new ImmutableJWKSet<>(jwkSet);
}
// 解析存储的jws
JWKSet jwkSet = JWKSet.parse(jwkSetCache);
return new ImmutableJWKSet<>(jwkSet);
}
附一下RedisConstants
类中的AUTHORIZATION_JWS_PREFIX_KEY
常量
/**
* jwk set缓存前缀
*/
public static final String AUTHORIZATION_JWS_PREFIX_KEY = "authorization_jws";
修改配置后启动认证服务,使用oauth2的几种方式获取一下token,然后重启服务携带token请求一下看看认证服务能否解析AccessToken,如果有什么问题可以在评论区中提出。
代码已提交至Gitee