虽然我同意这里其他答案的大部分内容,但没有其他答案真正回答了这个问题。
我将假设您在 Spring Boot 中使用带有 Redis 的 SpringSession。
为了使用 SpringSession,您可能已经(直接或间接)配置了一个扩展 SessionRepositoryFilter 的 servlet 过滤器。
SessionRepositoryFilter 使用SessionRepository。由于您使用的是 Redis,因此您的配置很可能使用了RedisOperationsSessionRepository。
RedisOperationsSessionRepository 实现 SessionRepository,正如您可能已经猜到的那样,它最终负责基于密钥(在您的情况下,密钥可能存储为用户浏览器上的 cookie)来获取、存储和删除会话)。
默认情况下,RedisOperationSessionRepository 使用实现了RedisSerializer 的JdkSerializationRedisSerializer 在将会话数据传递给 Redis 之前序列化会话数据。
根据RedisOperationSessionRepository 的文档,可以通过setDefaultSerializer 方法设置RedisOperationSessionRepository 将使用的默认序列化程序。
理论上你可以扩展JdkSerializationRedisSerializer 并在那里执行加密和解密。 JdkSerializationRedisSerializer 看起来像这样:
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
public Object deserialize(byte[] bytes) {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
public byte[] serialize(Object object) {
if (object == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
throw new SerializationException("Cannot serialize", ex);
}
}
}
因此添加加密的潜在方法可能如下所示:
public class CrypticRedisSerializer extends JdkSerializationRedisSerializer {
@Override
public Object deserialize(byte[] bytes) {
byte[] decrpyted;
try {
decrpyted = EncryptionUtils.decrypt(bytes);
return super.deserialize(decrpyted);
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// handle expections or allow to propagate, your choice!
return null;
}
@Override
public byte[] serialize(Object object) {
byte[] bytes = super.serialize(object);
try {
return EncryptionUtils.encrypt(bytes);
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// handle expections or allow to propagate, your choice!
return null;
}
}
EncrpytionUtils 可能如下所示:
public class EncryptionUtils {
private static SecretKeySpec skeySpec;
static {
try {
ClassPathResource res = new ClassPathResource("key.key");
if(res != null){
File file = res.getFile();
FileInputStream input = new FileInputStream(file);
byte[] in = new byte[(int)file.length()];
input.read(in);
skeySpec = new SecretKeySpec(in, "AES");
input.close();
}
}catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] encrypt(byte[] input)
throws GeneralSecurityException, NoSuchPaddingException{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(input);
}
public static byte[] decrypt(byte[] input) throws GeneralSecurityException, NoSuchPaddingException{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(input);
}
}
您需要做的就是确保将自定义序列化程序设置为RedisOperationSessionRepository 用户的默认值。
请注意:
- 上面的代码我没有测试过
- 我并不主张上述代码是一个理想的解决方案或THE 解决方案,而只是展示了一种使用Redis 将加密引入SpringSession 的机制。
- 显然,您可以使用任何您想要的 2 路加密算法。 EncrpytionUtils 只是一个例子。
- 这会影响性能。多少?没有测试很难说。请注意,这会对性能产生一些影响。
- 如果您真的担心加密发送到 Redis 的会话数据,那么我强烈建议您同时确保您的服务器是安全的。确保只有需要访问您的 Redis 服务器的服务器才能访问。将其放在防火墙后面。如果您使用的是 AWS 等云服务,请将您的 Redis 服务器放在 VPN 和私有子网内。 Check out this article.
-
Redis does not support connection encryption currently. 但是,就像他们建议的那样,您可以使用 Sniped 来确保您的连接是加密的。
查看文档和参考:
- SpringSession
RedisOperationsSessionRepository
SessionRepository