Redis、SpringBoot和HttpSession:我应该加密会话数据吗?
我正在使用Spring Boot 1.3.3构建一个web应用程序。我使用Redis处理会话 我将在Redis、SpringBoot和HttpSession:我应该加密会话数据吗?,session,redis,spring-boot,session-variables,spring-session,Session,Redis,Spring Boot,Session Variables,Spring Session,我正在使用Spring Boot 1.3.3构建一个web应用程序。我使用Redis处理会话 我将在HttpSession中设置一些“关键”数据,我想了解这将如何与Redis一起工作。是存储在服务器端的信息加上浏览器端的键,还是所有数据都在用户浏览器的cookie中 我想看看答案的文档参考,或者获得权威性的答案(例如,Pivotal dev)。创建者或redis提供的关于redis安全性的非常好的文章-读起来非常有趣。也请阅读评论 我很好奇的一件事是,您的“关键”数据是否必须存储在会话中?如果它
HttpSession
中设置一些“关键”数据,我想了解这将如何与Redis一起工作。是存储在服务器端的信息加上浏览器端的键,还是所有数据都在用户浏览器的cookie中
我想看看答案的文档参考,或者获得权威性的答案(例如,Pivotal dev)。创建者或redis提供的关于redis安全性的非常好的文章-读起来非常有趣。也请阅读评论
我很好奇的一件事是,您的“关键”数据是否必须存储在会话中?如果它对性能不是非常关键,您可以将其保存在数据库中。我在产品中使用redis只是为了存储令牌和基本用户数据,我见过有人将大数据量作为会话数据转储,这很好,但我认为这不是一个好主意 在我看来,您应该避免在redis中加密数据,否则会造成性能开销。因此,您可能希望将redis节点放在一个受保护的区域(内部),在该区域中,只允许来自应用程序的流量到达。如果不可能,则可以使用IPSec/Stunnel保护通信
顺便说一句,将会话数据存储为HTTPSession属性将比从Redis检索它更快。但我相信你会选择redis,可能是因为数据量太大。虽然我同意这里其他答案的大部分说法,但其他答案都没有回答这个问题。 我假设您正在SpringBoot中使用SpringSession和Redis 为了使用SpringSession,您可能(直接或间接)配置了一个servlet过滤器,该过滤器扩展了
SessionRepositoryFilter
SessionRepositoryFilter使用SessionRepository
。由于您使用的是Redis,因此您的配置很可能使用了RedisOperationsSessionRepository
RedisOperationsSessionRepository
实现了您可能猜到的SessionRepository
,并最终负责基于密钥(在您的情况下,密钥可能存储为用户浏览器上的cookie)获取、存储和删除会话
默认情况下,RedisOperationSessionRepository
使用实现了RedisSerializer
的JdkSerializationRedisSerializer
,在将所述数据交给Redis之前对会话数据进行序列化
根据的文档,可以通过其setDefaultSerializer
方法设置RedisOperationsSessionRepository
将使用的默认序列化程序
理论上,您可以扩展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);
}
}
}
其中EncrpytionUtils可能看起来像:
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;
}
}
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
用户的默认序列化程序
请注意:
我想存储一些“经典”的东西,比如代币、购物车、面向未注册用户的东西?如果是的话,我可以回答这个问题。让我知道。很好的回答!我唯一需要解决的问题是如何确保在使用SpringBoot和SpringSessionDataRedis时始终应用自定义序列化程序,后者在幕后神奇地配置了大部分相关组件。我最终得到了一个配置类,该类扩展了RedisHttpSessionConfiguration,并在PostConstruct方法中应用CrypticRedisSerializer实例。现在双向都可以了,谢谢你的样品。