Spring Session Redis 설정 건너뛰고
Spring Session Redis \xac\xed\x00\x05sr\x00\x0ejava.lang.Long 해결법은 2.7로
Web또는 API 서버에서 요청을 보내는자의 신분을 인증할 수 있는 장치가 없다면
Anonymous한 모든 유저가 서비스 접근 권한을 얻게되어 엄청난 일을 경험할 수 있다.
그렇기 때문에 헤더나 쿠키에 인증정보를 담아서 보내고 서버는 이것을 인증하고 권한을 부여한다.
이 때 보통 세션
, JWT 토큰
을 사용한다.
JSON Web Token
의 약자로 대칭키 방식으로 암호화 된 신분증
이라고 생각하면 편하다.
이 방식은 대칭키의 비밀키가 유출되었을 때 굉장히 취약한 방식으로 이를 방지하기 위한
복잡한 로직이 필요하다. 고려 사항이 많고 구현이 어렵다.
오늘은 세션이 메인이므로 자세한 건 구글
에게 물어보도록 하는걸로 하고 생략하겠다.
세션이란 말 그대로 유저가 인증요청을 했을 때 서버에서 세션을 생성하고
유저의 인증에 대한 상태관리를 하는 구조
를 말한다.
서버에서 유저의 인증에 대한 상태관리를 하기 때문에
Stateless
한 JWT와는 다르게 Stateful
한 방법이다.
장점으로는 구현이 어렵지 않고, 특정 상황에서 세션을 직접 핸들링할 수 있기 때문에
상황 대처가 더 용이하다.JWT는 탈취되면 기도메타
Redis(레디스)
란 Key-Value형태
의 비관계형 데이터베이스
다.
속도가 매우 빠른 장점이 있어, 캐시나 세션 저장소로 많이 사용한다.
오늘은 이 Redis를 스프링 부트와 함께 세션 저장소로 사용한 경험을 공유해보려고 한다.
나는 ubuntu서버를 사용하는데
$ sudo apt-get install redis-sever
를 통해 간단히 설치 가능하다.
꼭 비밀번호를 설정해준다
안해주면 바로 크롤러(?)같은 무언가가 들어와서 활개친다.
설정법은구글링
앞서 말한 세션을 생성하고, 관리하는 기능을 스프링에서 제공한다.
Spring Session
이다.
스프링 세션을 설정하면 Request에 대해서 세션을
(반)자동으로 생성하고 관리, 및 업데이트 해준다.
자동으로 관리해 주지만 설정을 해줘야 하기 때문.
같은 세션을 expire전에 접속하면 expire기한이 자동으로 늘어난다.
우선 나는 개발서버의 Spring Boot를 사용해 만들어진 API에 대해
세션을 적용해 보았고, 마찬가지로 개발서버에 redis를 설치했다.
나는 gradle
을 사용해서 build.gradle
에
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-data-redis'
을 추가해 주었다.
그 후
application.yml
의 로컬 개발 프로필에
spring:
profiles: local
redis:
host: XX.XX.XXX.XX
port: 6379
password: XXXXXXXXXX
host -> 레디스 서버 주소, 같은 서버라면 localhost
port -> 레디스의 기본 포트는 6379, 실제 서버에 적용시 바꿔준다.
password -> 레디스에 설정한 비밀번호
@NoArgsConstructor
@Configuration
@EnableRedisHttpSession()
public class RedisConfig{
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.password}")
private String redisPassword;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisHost);
redisStandaloneConfiguration.setPort(redisPort);
redisStandaloneConfiguration.setPassword(redisPassword);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
stringRedisTemplate.setDefaultSerializer(new StringRedisSerializer());
stringRedisTemplate.afterPropertiesSet();
return stringRedisTemplate;
}
}
@EnableRedisHttpSession()
은 메인 Application에 해줘도 무방하다.
위의 코드가 다들 기본적으로 Spring Session에 Redis를 사용하는 코드다.
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.password}")
private String redisPassword;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisHost);
redisStandaloneConfiguration.setPort(redisPort);
redisStandaloneConfiguration.setPassword(redisPassword);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
아까 application.yml에서 설정해준 redis-server에 관한 설정이다.
이를 통해 Spring Session이 쓸 redis-server를 연결한다.
어플리케이션 실행 시 redis연결 관련 에러라면 여기를 살펴본다.
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
stringRedisTemplate.setDefaultSerializer(new StringRedisSerializer());
stringRedisTemplate.afterPropertiesSet();
return stringRedisTemplate;
}
Redis에 데이터를 작성할 때는 룬문자(아닙니다)를 사용하는데
이것을 일반적인 형태로 바꿔주려면 serializer를 다른 걸로 설정해줘야한다.
@CrossOrigin
@PostMapping("/session/test")
public int sessionTest(HttpSession httpSession){
int number = 123;
httpSession.setAttribute(number);
return number;
}
여기서 파라미터로 HttpSession을 넣어주면 아무것도 해주지 않아도
위에서 설정한 Redis Config에 의해
자동으로 Redis에 세션이 생성된다.
실제 redis서버에 가서 redis-cli를 통해 데이터를 조회해본다.
데이터 조회 방법
1. redis 설치 서버 접속
2. redis-cli
3. auth "내가 설정한 비밀번호"
4. keys *
(Redis에 저장된 모든 데이터 보기) (처음 가면 String,hashMap 등 4가지 타입의 데이터가 있을텐데 자동 생성되는 데이터들이다.)
5. hgetall "spring:session:sessions:b76ff8d4-3f20-4179-b152-30de7d70190a"
( 4개 중에 이렇게 spring:session:sessions로 시작하는 데이터가 hashMap이고 이 안에 내가 custom하게 넣어준 데이터가 들어간다.)
1) "lastAccessedTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82Yd^~"
3) "creationTime"
4) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82Yd^~"
5) "sessionAttr:number"
6) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x13\x8f"
7) "maxInactiveInterval"
8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00v\xa7\x00"
근데 갑자기 룬문자를 발견한다.
중간에 sessionAttr:number
는 내가 custom하게 저장한 값이다.
보다싶이 숫자가 java.lang.Number
로 나온다. 나는 분명 int형 숫자를 저장했다.
여기서 꿀팁
Spring Session Redis \xac\xed\x00\x05sr\x00\x0ejava.lang.Long 해결법
물론 나만 몰랐을 수도 있는데 이렇게 설정하고
구글
한테 아무리 물어봐도 이렇게 나와있다.
스프링 부트에서 직접 읽어도 룬문자로 보인다.
대체 왜???????????
많이 헤맸다. 아까 설정해준 RedisConfig 파일에
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
을 추가해야한다. 이건 왜 아무도 안알려줘
아까 우리가 설정해준 serializer는 우리가 직접 데이터를 읽고 쓸 때의 설정을 해 준 것이고
이름만 봐도 Spring Boot가 세션 생성할 때 사용할 것 같은
springSessionDefaultRedisSerializer
가 자동으로 생성되는 Session의 Seriealizer였던 것이다.
이걸 추가해주면 정상적으로 데이터가 쓰여지고 읽을 수 있는걸 확인할 수 있다.