refactor(缓存): redistemplate / spring cache 使用 Json 序列化

sas 默认的缓存没有修改
This commit is contained in:
冷冷 committed 2024-08-22 08:17:55 +08:00
1 parent beb6474638
commit 2c53bcecf5
8 files changed
+184 -148

No files matched your search

@@ -19,6 +19,7 @@
package com.pig4cloud.pigx.common.core.util;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -96,6 +97,7 @@ public class R<T> implements Serializable {
return apiResult;
}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public boolean isOk() {
return this.code == CommonConstants.SUCCESS;
}
@@ -17,6 +17,9 @@
package com.pig4cloud.pigx.common.data.cache;
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pig4cloud.pigx.common.core.jackson.PigxJavaTimeModule;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
@@ -31,8 +34,8 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.lang.Nullable;
import java.util.LinkedHashMap;
@@ -89,8 +92,13 @@ public class RedisCacheAutoConfiguration {
else {
CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisSerializer<Object> jsonRedisSerializer = RedisSerializer.json();
ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(jsonRedisSerializer, "mapper");
objectMapper.registerModules(new PigxJavaTimeModule());
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
.fromSerializer(jsonRedisSerializer));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
@@ -17,6 +17,9 @@
package com.pig4cloud.pigx.common.data.cache;
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pig4cloud.pigx.common.core.jackson.PigxJavaTimeModule;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
@@ -43,8 +46,14 @@ public class RedisTemplateConfiguration {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
redisTemplate.setHashValueSerializer(RedisSerializer.java());
// 设置 redisTemplate 的序列化器为 json
RedisSerializer<Object> jsonRedisSerializer = RedisSerializer.json();
ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(jsonRedisSerializer, "mapper");
objectMapper.registerModules(new PigxJavaTimeModule());
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
@@ -17,6 +17,7 @@
package com.pig4cloud.pigx.common.data.tenant;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.admin.api.entity.SysTenant;
@@ -40,7 +41,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
/**
* @author lengleng
@@ -100,12 +100,11 @@ public class TenantContextHolderFilter extends GenericFilterBean {
return true;
}
Cache.ValueWrapper valueWrapper = cache.get(SimpleKey.EMPTY);
if (Objects.isNull(valueWrapper)) {
List<SysTenant> tenantList = cache.get(SimpleKey.EMPTY, List.class);
if (CollUtil.isEmpty(tenantList)) {
return true;
}
List<SysTenant> tenantList = (List<SysTenant>) valueWrapper.get();
boolean exist = tenantList.stream().anyMatch(tenant -> NumberUtil.equals(tenant.getId(), TenantContextHolder.getTenantId()));
if (exist) {
return true;
@@ -22,6 +22,7 @@ import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.RetOps;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -31,6 +32,9 @@ import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Objects;
import java.util.Optional;
/**
* 用户详细信息 default
*
@@ -41,47 +45,49 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
@RequiredArgsConstructor
public class PigxDefaultUserDetailsServiceImpl implements PigxUserDetailsService {
private final RemoteUserService remoteUserService;
private final RemoteUserService remoteUserService;
private final CacheManager cacheManager;
private final CacheManager cacheManager;
/**
* 用户密码登录
* @param username 用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
if (cache != null && cache.get(username) != null) {
return cache.get(username, PigxUser.class);
}
/**
* 用户密码登录
*
* @param username 用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
if (Objects.nonNull(cache) && Objects.nonNull(cache.get(username))) {
return getUserDetails(Optional.ofNullable(cache.get(username, UserInfo.class)));
}
R<UserInfo> result = remoteUserService.info(username);
UserDetails userDetails = getUserDetails(result);
cache.put(username, userDetails);
return userDetails;
}
R<UserInfo> result = remoteUserService.info(username);
Optional<UserInfo> userInfoOptional = RetOps.of(result).getData();
userInfoOptional.ifPresent(userInfo -> Objects.requireNonNull(cache).put(username, userInfo));
return getUserDetails(userInfoOptional);
}
/**
* 通过用户实体查询
* @param pigxUser user
* @return
*/
@Override
public UserDetails loadUserByUser(PigxUser pigxUser) {
// 避免 C端用户通过接口调用B端接口的安全问题
if (UserTypeEnum.TOB.getStatus().equals(pigxUser.getUserType())) {
return loadUserByUsername(pigxUser.getUsername());
}
return null;
}
/**
* 通过用户实体查询
*
* @param pigxUser user
* @return
*/
@Override
public UserDetails loadUserByUser(PigxUser pigxUser) {
// 避免 C端用户通过接口调用B端接口的安全问题
if (UserTypeEnum.TOB.getStatus().equals(pigxUser.getUserType())) {
return loadUserByUsername(pigxUser.getUsername());
}
return null;
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
}
@@ -4,6 +4,7 @@ import com.pig4cloud.pigx.admin.api.dto.UserInfo;
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.RetOps;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -25,7 +26,7 @@ public class PigxMobileUserDetailServiceImpl implements PigxUserDetailsService {
@SneakyThrows
public UserDetails loadUserByUsername(String phone) {
R<UserInfo> result = remoteUserService.social(phone);
return getUserDetails(result);
return getUserDetails(RetOps.of(result).getData());
}
@Override
@@ -10,7 +10,8 @@ import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.RetOps;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2Error;
@@ -24,6 +25,7 @@ import org.springframework.security.oauth2.server.authorization.settings.TokenSe
import java.time.Duration;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
@@ -35,107 +37,123 @@ import java.util.Optional;
@RequiredArgsConstructor
public class PigxRemoteRegisteredClientRepository implements RegisteredClientRepository {
/**
* 刷新令牌有效期默认 30 天
*/
private final static int refreshTokenValiditySeconds = 60 * 60 * 24 * 30;
/**
* 刷新令牌有效期默认 30 天
*/
private final static int refreshTokenValiditySeconds = 60 * 60 * 24 * 30;
/**
* 请求令牌有效期默认 12 小时
*/
private final static int accessTokenValiditySeconds = 60 * 60 * 12;
/**
* 请求令牌有效期默认 12 小时
*/
private final static int accessTokenValiditySeconds = 60 * 60 * 12;
private final RemoteClientDetailsService clientDetailsService;
private final RemoteClientDetailsService clientDetailsService;
/**
* Saves the registered client.
*
* <p>
* IMPORTANT: Sensitive information should be encoded externally from the
* implementation, e.g. {@link RegisteredClient#getClientSecret()}
* @param registeredClient the {@link RegisteredClient}
*/
@Override
public void save(RegisteredClient registeredClient) {
}
private final CacheManager cacheManager;
/**
* Returns the registered client identified by the provided {@code id}, or
* {@code null} if not found.
* @param id the registration identifier
* @return the {@link RegisteredClient} if found, otherwise {@code null}
*/
@Override
public RegisteredClient findById(String id) {
throw new UnsupportedOperationException();
}
/**
* Saves the registered client.
*
* <p>
* IMPORTANT: Sensitive information should be encoded externally from the
* implementation, e.g. {@link RegisteredClient#getClientSecret()}
*
* @param registeredClient the {@link RegisteredClient}
*/
@Override
public void save(RegisteredClient registeredClient) {
}
/**
* Returns the registered client identified by the provided {@code clientId}, or
* {@code null} if not found.
* @param clientId the client identifier
* @return the {@link RegisteredClient} if found, otherwise {@code null}
*/
/**
* Returns the registered client identified by the provided {@code id}, or
* {@code null} if not found.
*
* @param id the registration identifier
* @return the {@link RegisteredClient} if found, otherwise {@code null}
*/
@Override
public RegisteredClient findById(String id) {
throw new UnsupportedOperationException();
}
/**
* 重写原生方法支持redis缓存
* @param clientId
* @return
*/
@Override
@SneakyThrows
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
public RegisteredClient findByClientId(String clientId) {
/**
* Returns the registered client identified by the provided {@code clientId}, or
* {@code null} if not found.
* @param clientId the client identifier
* @return the {@link RegisteredClient} if found, otherwise {@code null}
*/
SysOauthClientDetails clientDetails = RetOps
.of(clientDetailsService.getClientDetailsById(clientId))
.getData()
.orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException(
new OAuth2Error("客户端查询异常,请检查数据库链接"), null));
/**
* 按客户端 ID 查找
*
* @param clientId 客户端 ID
* @return {@link RegisteredClient }
*/
@Override
@SneakyThrows
public RegisteredClient findByClientId(String clientId) {
RegisteredClient.Builder builder = RegisteredClient.withId(clientDetails.getClientId())
.clientId(clientDetails.getClientId())
.clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret())
.clientAuthenticationMethods(clientAuthenticationMethods -> {
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
});
// 授权模式
Arrays.stream(clientDetails.getAuthorizedGrantTypes())
.forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant)));
Cache cache = cacheManager.getCache(CacheConstants.CLIENT_DETAILS_KEY);
SysOauthClientDetails clientDetails = null;
if (Objects.nonNull(cache) && Objects.nonNull(cache.get(clientId))) {
clientDetails = cache.get(clientId, SysOauthClientDetails.class);
}
// 回调地址
Optional.ofNullable(clientDetails.getWebServerRedirectUri())
.ifPresent(redirectUri -> Arrays.stream(redirectUri.split(StrUtil.COMMA))
.filter(StrUtil::isNotBlank)
.forEach(builder::redirectUri));
if (Objects.isNull(clientDetails)) {
SysOauthClientDetails fetchedClientDetails = RetOps
.of(clientDetailsService.getClientDetailsById(clientId))
.getData()
.orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException(
new OAuth2Error("客户端查询异常,请检查数据库链接"), null));
// scope
Optional.ofNullable(clientDetails.getScope())
.ifPresent(scope -> Arrays.stream(scope.split(StrUtil.COMMA))
.filter(StrUtil::isNotBlank)
.forEach(builder::scope));
clientDetails = fetchedClientDetails;
Optional.ofNullable(cache).ifPresent(c -> c.put(clientId, fetchedClientDetails));
}
// 注入扩展配置
Optional.ofNullable(clientDetails.getAdditionalInformation()).ifPresent(ext -> {
Map map = JSONUtil.parseObj(ext).toBean(Map.class);
builder.clientSettings(ClientSettings.withSettings(map)
.requireProofKey(false)
.requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove()))
.build());
});
final SysOauthClientDetails finalClientDetails = clientDetails;
return builder
.tokenSettings(TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
.accessTokenTimeToLive(Duration.ofSeconds(
Optional.ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
.refreshTokenTimeToLive(Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
.orElse(refreshTokenValiditySeconds)))
.build())
RegisteredClient.Builder builder = RegisteredClient.withId(finalClientDetails.getClientId())
.clientId(finalClientDetails.getClientId())
.clientSecret(SecurityConstants.NOOP + finalClientDetails.getClientSecret())
.clientAuthenticationMethods(clientAuthenticationMethods -> {
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
});
.build();
// 授权模式
Arrays.stream(finalClientDetails.getAuthorizedGrantTypes())
.forEach(grant -> builder.authorizationGrantType(new AuthorizationGrantType(grant)));
}
// 回调地址
Optional.ofNullable(finalClientDetails.getWebServerRedirectUri())
.ifPresent(redirectUri -> Arrays.stream(redirectUri.split(StrUtil.COMMA))
.filter(StrUtil::isNotBlank)
.forEach(builder::redirectUri));
// scope
Optional.ofNullable(finalClientDetails.getScope())
.ifPresent(scope -> Arrays.stream(scope.split(StrUtil.COMMA))
.filter(StrUtil::isNotBlank)
.forEach(builder::scope));
// 注入扩展配置
Optional.ofNullable(finalClientDetails.getAdditionalInformation()).ifPresent(ext -> {
Map<String, Object> map = JSONUtil.parseObj(ext).toBean(Map.class);
builder.clientSettings(ClientSettings.withSettings(map)
.requireProofKey(false)
.requireAuthorizationConsent(!BooleanUtil.toBoolean(finalClientDetails.getAutoapprove()))
.build());
});
return builder
.tokenSettings(TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
.accessTokenTimeToLive(Duration.ofSeconds(
Optional.ofNullable(finalClientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
.refreshTokenTimeToLive(Duration.ofSeconds(Optional.ofNullable(finalClientDetails.getRefreshTokenValidity())
.orElse(refreshTokenValiditySeconds)))
.build())
.build();
}
}
@@ -22,8 +22,6 @@ import com.pig4cloud.pigx.admin.api.entity.SysUser;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.RetOps;
import org.springframework.core.Ordered;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
@@ -31,10 +29,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
/**
* @author lengleng
@@ -63,16 +58,14 @@ public interface PigxUserDetailsService extends UserDetailsService, Ordered {
}
/**
* 构建userdetails
* 获取用户详细信息
*
* @param result 用户信息
* @return UserDetails
* @throws UsernameNotFoundException
* @param userInfoOptional 用户信息:可选
* @return {@link UserDetails }
*/
default UserDetails getUserDetails(R<UserInfo> result) {
default UserDetails getUserDetails(Optional<UserInfo> userInfoOptional) {
// @formatter:off
return RetOps.of(result)
.getData()
return userInfoOptional
.map(this::convertUserDetails)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
// @formatter:on