init
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.newyetai.security.social.service.CommonOAuth2Provider;
|
||||
|
||||
/**
|
||||
* Adapter class to convert {@link OAuth2ClientProperties} to a
|
||||
* {@link ClientRegistration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Thiago Hirata
|
||||
* @author Madhura Bhave
|
||||
* @author MyeongHyeon Lee
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public final class OAuth2ClientPropertiesRegistrationAdapter {
|
||||
|
||||
private OAuth2ClientPropertiesRegistrationAdapter() {
|
||||
}
|
||||
|
||||
public static Map<String, ClientRegistration> getClientRegistrations(OAuth2ClientProperties properties) {
|
||||
Map<String, ClientRegistration> clientRegistrations = new HashMap<>();
|
||||
properties.getRegistration().forEach((key, value) -> clientRegistrations.put(key,
|
||||
getClientRegistration(key, value, properties.getProvider())));
|
||||
return clientRegistrations;
|
||||
}
|
||||
|
||||
private static ClientRegistration getClientRegistration(String registrationId,
|
||||
OAuth2ClientProperties.Registration properties, Map<String, Provider> providers) {
|
||||
Builder builder = getBuilderFromIssuerIfPossible(registrationId, properties.getProvider(), providers);
|
||||
if (builder == null) {
|
||||
builder = getBuilder(registrationId, properties.getProvider(), providers);
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::getClientId).to(builder::clientId);
|
||||
map.from(properties::getClientSecret).to(builder::clientSecret);
|
||||
map.from(properties::getClientAuthenticationMethod).as(ClientAuthenticationMethod::new)
|
||||
.to(builder::clientAuthenticationMethod);
|
||||
map.from(properties::getAuthorizationGrantType).as(AuthorizationGrantType::new)
|
||||
.to(builder::authorizationGrantType);
|
||||
map.from(properties::getRedirectUri).to(builder::redirectUri);
|
||||
map.from(properties::getScope).as(StringUtils::toStringArray).to(builder::scope);
|
||||
map.from(properties::getClientName).to(builder::clientName);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Builder getBuilderFromIssuerIfPossible(String registrationId, String configuredProviderId,
|
||||
Map<String, Provider> providers) {
|
||||
String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;
|
||||
if (providers.containsKey(providerId)) {
|
||||
Provider provider = providers.get(providerId);
|
||||
String issuer = provider.getIssuerUri();
|
||||
if (issuer != null) {
|
||||
Builder builder = ClientRegistrations.fromIssuerLocation(issuer).registrationId(registrationId);
|
||||
return getBuilder(builder, provider);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Builder getBuilder(String registrationId, String configuredProviderId,
|
||||
Map<String, Provider> providers) {
|
||||
String providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;
|
||||
CommonOAuth2Provider provider = getCommonProvider(providerId);
|
||||
if (provider == null && !providers.containsKey(providerId)) {
|
||||
throw new IllegalStateException(getErrorMessage(configuredProviderId, registrationId));
|
||||
}
|
||||
Builder builder = (provider != null) ? provider.getBuilder(registrationId)
|
||||
: ClientRegistration.withRegistrationId(registrationId);
|
||||
if (providers.containsKey(providerId)) {
|
||||
return getBuilder(builder, providers.get(providerId));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static String getErrorMessage(String configuredProviderId, String registrationId) {
|
||||
return ((configuredProviderId != null) ? "Unknown provider ID '" + configuredProviderId + "'"
|
||||
: "Provider ID must be specified for client registration '" + registrationId + "'");
|
||||
}
|
||||
|
||||
private static Builder getBuilder(Builder builder, Provider provider) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(provider::getAuthorizationUri).to(builder::authorizationUri);
|
||||
map.from(provider::getTokenUri).to(builder::tokenUri);
|
||||
map.from(provider::getUserInfoUri).to(builder::userInfoUri);
|
||||
map.from(provider::getUserInfoAuthenticationMethod).as(AuthenticationMethod::new)
|
||||
.to(builder::userInfoAuthenticationMethod);
|
||||
map.from(provider::getJwkSetUri).to(builder::jwkSetUri);
|
||||
map.from(provider::getUserNameAttribute).to(builder::userNameAttributeName);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static CommonOAuth2Provider getCommonProvider(String providerId) {
|
||||
try {
|
||||
return ApplicationConversionService.getSharedInstance().convert(providerId, CommonOAuth2Provider.class);
|
||||
}
|
||||
catch (ConversionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
381
src/main/java/org/springframework/data/redis/cache/RedisCache.java
vendored
Normal file
381
src/main/java/org/springframework/data/redis/cache/RedisCache.java
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
package org.springframework.data.redis.cache;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.cache.support.AbstractValueAdaptingCache;
|
||||
import org.springframework.cache.support.NullValue;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cache.Cache} implementation using for Redis as underlying store.
|
||||
* <p/>
|
||||
* Use {@link RedisCacheManager} to create {@link RedisCache} instances.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @see RedisCacheConfiguration
|
||||
* @see RedisCacheWriter
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RedisCache extends AbstractValueAdaptingCache {
|
||||
|
||||
private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
|
||||
|
||||
private final String name;
|
||||
private final RedisCacheWriter cacheWriter;
|
||||
private final RedisCacheConfiguration cacheConfig;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* Create new {@link RedisCache}.
|
||||
*
|
||||
* @param name must not be {@literal null}.
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param cacheConfig must not be {@literal null}.
|
||||
*/
|
||||
protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
|
||||
|
||||
super(cacheConfig.getAllowCacheNullValues());
|
||||
|
||||
Assert.notNull(name, "Name must not be null!");
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
|
||||
Assert.notNull(cacheConfig, "CacheConfig must not be null!");
|
||||
|
||||
this.name = name;
|
||||
this.cacheWriter = cacheWriter;
|
||||
this.cacheConfig = cacheConfig;
|
||||
this.conversionService = cacheConfig.getConversionService();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.support.AbstractValueAdaptingCache#lookup(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected Object lookup(Object key) {
|
||||
|
||||
byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return deserializeCacheValue(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#getNativeCache()
|
||||
*/
|
||||
@Override
|
||||
public RedisCacheWriter getNativeCache() {
|
||||
return this.cacheWriter;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#get(java.lang.Object, java.util.concurrent.Callable)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized <T> T get(Object key, Callable<T> valueLoader) {
|
||||
|
||||
ValueWrapper result = get(key);
|
||||
|
||||
if (result != null) {
|
||||
return (T) result.get();
|
||||
}
|
||||
|
||||
T value = valueFromLoader(key, valueLoader);
|
||||
put(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void put(Object key, @Nullable Object value) {
|
||||
|
||||
Object cacheValue = preProcessCacheValue(value);
|
||||
|
||||
if (!isAllowNullValues() && cacheValue == null) {
|
||||
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
|
||||
name));
|
||||
}
|
||||
|
||||
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
|
||||
|
||||
Object cacheValue = preProcessCacheValue(value);
|
||||
|
||||
if (!isAllowNullValues() && cacheValue == null) {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
byte[] result = cacheWriter.putIfAbsent(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
|
||||
cacheConfig.getTtl());
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result)));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#evict(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void evict(Object key) {
|
||||
// 通配符删除
|
||||
if (key instanceof String && ((String) key).endsWith("*")) {
|
||||
byte[] pattern = conversionService.convert(createCacheKey(key), byte[].class);
|
||||
this.cacheWriter.clean(this.name, pattern);
|
||||
return ;
|
||||
}
|
||||
cacheWriter.remove(name, createAndConvertCacheKey(key));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.cache.Cache#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
|
||||
cacheWriter.clean(name, pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheStatistics} snapshot for this cache instance. Statistics are accumulated per cache instance
|
||||
* and not from the backing Redis data store.
|
||||
*
|
||||
* @return statistics object for this {@link RedisCache}.
|
||||
* @since 2.4
|
||||
*/
|
||||
public CacheStatistics getStatistics() {
|
||||
return cacheWriter.getCacheStatistics(getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all statistics counters and gauges for this cache.
|
||||
*
|
||||
* @since 2.4
|
||||
*/
|
||||
public void clearStatistics() {
|
||||
cacheWriter.clearStatistics(getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link RedisCacheConfiguration} used.
|
||||
*
|
||||
* @return immutable {@link RedisCacheConfiguration}. Never {@literal null}.
|
||||
*/
|
||||
public RedisCacheConfiguration getCacheConfiguration() {
|
||||
return cacheConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook called before passing object to
|
||||
* {@link org.springframework.data.redis.serializer.RedisSerializer}.
|
||||
*
|
||||
* @param value can be {@literal null}.
|
||||
* @return preprocessed value. Can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object preProcessCacheValue(@Nullable Object value) {
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return isAllowNullValues() ? NullValue.INSTANCE : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the key.
|
||||
*
|
||||
* @param cacheKey must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected byte[] serializeCacheKey(String cacheKey) {
|
||||
return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the value to cache.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected byte[] serializeCacheValue(Object value) {
|
||||
|
||||
if (isAllowNullValues() && value instanceof NullValue) {
|
||||
return BINARY_NULL_VALUE;
|
||||
}
|
||||
|
||||
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the given value to the actual cache value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object deserializeCacheValue(byte[] value) {
|
||||
|
||||
if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
|
||||
return NullValue.INSTANCE;
|
||||
}
|
||||
|
||||
return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook for creating cache key before it gets serialized.
|
||||
*
|
||||
* @param key will never be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected String createCacheKey(Object key) {
|
||||
|
||||
String convertedKey = convertKey(key);
|
||||
|
||||
if (!cacheConfig.usePrefix()) {
|
||||
return convertedKey;
|
||||
}
|
||||
|
||||
return prefixCacheKey(convertedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {@code key} to a {@link String} representation used for cache key creation.
|
||||
*
|
||||
* @param key will never be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @throws IllegalStateException if {@code key} cannot be converted to {@link String}.
|
||||
*/
|
||||
protected String convertKey(Object key) {
|
||||
|
||||
if (key instanceof String) {
|
||||
return (String) key;
|
||||
}
|
||||
|
||||
TypeDescriptor source = TypeDescriptor.forObject(key);
|
||||
|
||||
if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
|
||||
try {
|
||||
return conversionService.convert(key, String.class);
|
||||
} catch (ConversionFailedException e) {
|
||||
|
||||
// may fail if the given key is a collection
|
||||
if (isCollectionLikeOrMap(source)) {
|
||||
return convertCollectionLikeOrMapKey(key, source);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
|
||||
|
||||
if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot convert cache key %s to String. Please register a suitable Converter via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'.",
|
||||
source, key.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
private String convertCollectionLikeOrMapKey(Object key, TypeDescriptor source) {
|
||||
|
||||
if (source.isMap()) {
|
||||
|
||||
StringBuilder target = new StringBuilder("{");
|
||||
|
||||
for (Entry<?, ?> entry : ((Map<?, ?>) key).entrySet()) {
|
||||
target.append(convertKey(entry.getKey())).append("=").append(convertKey(entry.getValue()));
|
||||
}
|
||||
target.append("}");
|
||||
|
||||
return target.toString();
|
||||
} else if (source.isCollection() || source.isArray()) {
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
|
||||
Collection<?> collection = source.isCollection() ? (Collection<?>) key
|
||||
: Arrays.asList(ObjectUtils.toObjectArray(key));
|
||||
|
||||
for (Object val : collection) {
|
||||
sj.add(convertKey(val));
|
||||
}
|
||||
return "[" + sj.toString() + "]";
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Cannot convert cache key %s to String.", key));
|
||||
}
|
||||
|
||||
private boolean isCollectionLikeOrMap(TypeDescriptor source) {
|
||||
return source.isArray() || source.isCollection() || source.isMap();
|
||||
}
|
||||
|
||||
private byte[] createAndConvertCacheKey(Object key) {
|
||||
return serializeCacheKey(createCacheKey(key));
|
||||
}
|
||||
|
||||
private String prefixCacheKey(String key) {
|
||||
|
||||
// allow contextual cache names by computing the key prefix on every call.
|
||||
return cacheConfig.getKeyPrefixFor(name) + key;
|
||||
}
|
||||
|
||||
private static <T> T valueFromLoader(Object key, Callable<T> valueLoader) {
|
||||
|
||||
try {
|
||||
return valueLoader.call();
|
||||
} catch (Exception e) {
|
||||
throw new ValueRetrievalException(key, valueLoader, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user