- 세션을 저장할 레디스를 설치하고 연동해야 합니다.
- 레디스 설치 글을 참고해 연동 작업을 진행하면 됩니다.
build.gradle
// spring에서 redis에 대한 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// spring에서 redis를 session storage로 사용하기 위한 의존성
implementation 'org.springframework.session:spring-session-data-redis'
1) spring-boot-starter-data-redis는 spring
2) spring-session-data-redis
application.yml
spring :
session :
storage-type : redis
redis :
host : localhost
password: password
port : 6379
main-Application
에 @EnableRedisHttpSession
설정을 추가합니다.
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
public String host;
@Value("${spring.redis.password}")
public String password;
@Value("${spring.redis.port}")
public int port;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(host);
configuration.setPassword(password);
// password 설정을 추가합니다.
configuration.setPort(port);
return new LettuceConnectionFactory(configuration);
}
}
(+) 발생한 에러
method failed; nested exception is org.springframework.data.redis.redisconnectionfailureexception: unable to connect to redis; nested exception is io.lettuce.core.redisconnectionexception: unable to connect to localhost:6379
configuration.setPassword(password);
- 사용자가 로그인 시 생성되는 세션을 redis 에 등록시켜주고 없애주어야 합니다.
- 저의 설계는 아래와 같습니다.
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
public String host;
@Value("${spring.redis.password}")
public String password;
@Value("${spring.redis.port}")
public int port;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
// stringRedisTemplate bean은 일반적인 String 값을 key, value로 사용하는 경우 사용합니다.
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new StringRedisSerializer());
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}
// Redis 서버와의 통신을 위한 low-level 추상화를 제공합니다.
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(host);
configuration.setPassword(password);
configuration.setPort(port);
return new LettuceConnectionFactory(configuration);
}
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class RedisService {
private final StringRedisTemplate stringRedisTemplate;
/**
* id 포함 여부를 판단합니다.
* @param chkId : 포함 여부를 판단할 id
* @return : 포함될 시 true , 아니라면 false
*/
public boolean ifIdInLoginList(Long chkId) {
boolean if_in_login_list = false;
Set<byte[]> keys = stringRedisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
Iterator<byte[]> it = keys.iterator();
while (it.hasNext()) {
byte[] data = it.next();
if (chkId.toString().equals(new String(data, 0, data.length))) {
if_in_login_list = true;
break;
}
}
return if_in_login_list;
}
/**
* 현재 유저 수 (key 갯수) 를 count 합니다.
* @return 현재 유저 수 (key 갯수)
*/
public int getAllRedisStringValue() {
// key의 Set을 가져옵니다.
Set<byte[]> keys = stringRedisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
// 현재 유저 수 (key 갯수) 를 count 합니다.
Iterator<byte[]> it = keys.iterator();
int simultaneous_user_count = 0;
while (it.hasNext()) {
simultaneous_user_count += 1;
}
return simultaneous_user_count;
}
public boolean isUserMoreThan50() {
return getAllRedisStringValue() > 49;
}
/**
* key = member id
* 세션 id를 생성하고, 값을 세션에 저장합니다.
* @param key = member id
*/
public void setRedisStringValue(String key) {
// 세션 id를 생성하고, 값에 세션을 저장한 후 Redis의 key-value 형태로 저장합니다.
String sessionId = UUID.randomUUID().toString();
// opsForValue() = String 을 쉽게 Serialize / Deserialize 해주는 Interface
ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
// key 로 사용자 id, 값으로 session id 를 set 합니다.
stringValueOperations.set(key, sessionId);
}
/**
* key = member id to be deleted
* 로그아웃 시 멤버의 id key값을 제거합니다.
* @param key = member id
*/
public void removeKey(String key) {
stringRedisTemplate.delete(key);
}
}
Set<byte[]> keys = RedisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
/**
* 현재 유저 수 (key 갯수) 를 count 합니다.
* @return 현재 유저 수 (key 갯수)
*/
public int getAllRedisStringValue() {
// key의 Set을 가져옵니다.
Set<byte[]> keys = stringRedisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
// 현재 유저 수 (key 갯수) 를 count 합니다.
Iterator<byte[]> it = keys.iterator();
int simultaneous_user_count = 0;
while (it.hasNext()) {
simultaneous_user_count += 1;
}
return simultaneous_user_count;
}
public boolean isUserMoreThan50() {
return getAllRedisStringValue() > 49;
}
// 키 값이 50명 이상이면 false 반환
public boolean isUserMoreThan50(){
if(getAllRedisStringValue()>50){
return true;
}else{
return false;
}
}
유저 아디 키값 : 세션아이디
를 레디스에 set 해줍니다. 지정된 사용자 수 (여기선 50명)
가 넘는지 여부를 검사해서 false가 반환될 때만 허용해줍니다.isUserMoreThan50
문으로 검사 진행해주고, setRedisStringValue
메소드를 통해 레디스에 유저 id를 set 해줍니다.
@Transactional(readOnly = true)
public SignInResponse signIn(SignInRequest req) {
if (redisService.isUserMoreThan50()) {
// 50명 넘으면 에러 던지며 로그인을 허용하지 않습니다.
throw new MemberSoManyException();
}
/**
* key = member id
* 세션 id를 생성하고, 값을 세션에 저장합니다.
* @param key = member id
*/
public void setRedisStringValue(String key) {
// 세션 id를 생성하고, 값에 세션을 저장한 후 Redis의 key-value 형태로 저장합니다.
String sessionId = UUID.randomUUID().toString();
// opsForValue() = String 을 쉽게 Serialize / Deserialize 해주는 Interface
ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
// key 로 사용자 id, 값으로 session id 를 set 합니다.
stringValueOperations.set(key, sessionId);
}
}
RedisService
의 removeKey(로그아웃할 id)
를 추가함으로써 redis에서 유저 id로 된 key값을 제거합니다.
@CrossOrigin(origins = "https://localhost:3000")
@PostMapping("/logout/{id}")
@ResponseBody
public Response signOut(@PathVariable Long id) {
redisService.removeKey(id.toString());
return Response.success(id);
}
/**
* key = member id to be deleted
* 로그아웃 시 멤버의 id key값을 제거합니다.
* @param key = member id
*/
public void removeKey(String key) {
stringRedisTemplate.delete(key);
}