fix: 缓存JWKSet(JWT密钥对)至Redis,避免每次重新启动服务导致之前签发的JWT失效

This commit is contained in:
haoxr 2024-01-29 10:49:14 +08:00
parent 143ae5bb6c
commit 722d1e026f

View File

@ -2,6 +2,7 @@
package com.youlai.auth.config; package com.youlai.auth.config;
import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.JWKSet;
@ -25,7 +26,9 @@ import com.youlai.auth.handler.MyAuthenticationSuccessHandler;
import com.youlai.auth.service.MemberDetailsService; import com.youlai.auth.service.MemberDetailsService;
import com.youlai.auth.model.SysUserDetails; import com.youlai.auth.model.SysUserDetails;
import com.youlai.auth.jackson.SysUserMixin; import com.youlai.auth.jackson.SysUserMixin;
import com.youlai.common.constant.SecurityConstants;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -142,21 +145,44 @@ public class AuthorizationServerConfig {
} }
/**
* JWKJWT密钥对
*/
@Bean // <5> @Bean // <5>
@SneakyThrows
public JWKSource<SecurityContext> jwkSource() { public JWKSource<SecurityContext> jwkSource() {
// 尝试从Redis中获取JWKSet(JWT密钥对包含非对称加密的公钥和私钥)
String jwkSetStr = redisTemplate.opsForValue().get(SecurityConstants.JWK_SET_KEY);
if (StrUtil.isNotBlank(jwkSetStr)) {
// 如果存在解析JWKSet并返回
JWKSet jwkSet = JWKSet.parse(jwkSetStr);
return new ImmutableJWKSet<>(jwkSet);
} else {
// 如果Redis中不存在JWKSet生成新的JWKSet
KeyPair keyPair = generateRsaKey(); KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// @formatter:off
// 构建RSAKey
RSAKey rsaKey = new RSAKey.Builder(publicKey) RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey) .privateKey(privateKey)
.keyID(UUID.randomUUID().toString()) .keyID(UUID.randomUUID().toString())
.build(); .build();
// @formatter:on
// 构建JWKSet
JWKSet jwkSet = new JWKSet(rsaKey); JWKSet jwkSet = new JWKSet(rsaKey);
// 将JWKSet存储在Redis中
redisTemplate.opsForValue().set(SecurityConstants.JWK_SET_KEY, jwkSet.toString(Boolean.FALSE));
return new ImmutableJWKSet<>(jwkSet); return new ImmutableJWKSet<>(jwkSet);
} }
}
/**
* 生成RSA密钥对
*/
private static KeyPair generateRsaKey() { // <6> private static KeyPair generateRsaKey() { // <6>
KeyPair keyPair; KeyPair keyPair;
try { try {
@ -199,8 +225,10 @@ public class AuthorizationServerConfig {
@Bean @Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) { RegisteredClientRepository registeredClientRepository) {
// 创建基于JDBC的OAuth2授权服务这个服务使用JdbcTemplate和客户端仓库来存储和检索OAuth2授权数据
JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
// 创建并配置用于处理数据库中OAuth2授权数据的行映射器
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository); JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
rowMapper.setLobHandler(new DefaultLobHandler()); rowMapper.setLobHandler(new DefaultLobHandler());
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
@ -209,9 +237,16 @@ public class AuthorizationServerConfig {
objectMapper.registerModules(securityModules); objectMapper.registerModules(securityModules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module()); objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
// You will need to write the Mixin for your class so Jackson can marshall it. // You will need to write the Mixin for your class so Jackson can marshall it.
// 添加自定义Mixin用于序列化/反序列化特定的类
// Mixin类需要自行实现以便Jackson可以处理这些类的序列化
objectMapper.addMixIn(SysUserDetails.class, SysUserMixin.class); objectMapper.addMixIn(SysUserDetails.class, SysUserMixin.class);
objectMapper.addMixIn(Long.class, Object.class); objectMapper.addMixIn(Long.class, Object.class);
// 将配置好的ObjectMapper设置到行映射器中
rowMapper.setObjectMapper(objectMapper); rowMapper.setObjectMapper(objectMapper);
// 将自定义的行映射器设置到授权服务中
service.setAuthorizationRowMapper(rowMapper); service.setAuthorizationRowMapper(rowMapper);
return service; return service;