스프링부트 2.7.x , gradle
java 11
기본 게시판을 만들던 중
세션을 레디스에 띄어 놓기 위해서. 레디스와 스프링부트를 연결해 세션정보를 레디스에서 관리하도록 해보겠습니다.
레디스는 도커 를 사용 해보겠습니다.
1. docker ps
- 현재 가동중인 도커를 보여줍니다. 레디스가 없으면
2. docker run -d -p 6379:6379 redis
- 레디스를 받아주시구요
3. 다시 docker ps
- 레디스에 containerID 가나옵니다.
4. 레디스 실행
docker exec -it f8bccab9b30c /bin/sh 를 해주면
# 으로 바뀌어야 합니다.
도커를 확인해보면 이렇게 레디스가 돌아가고 있습니다. 포트는 6379 이고요
이제 스프링과 이어보도록하겠습니다.
0. build.gradle추가, @EnableRedisHttpSession 추가
// session 스토리지를 redis에 저장하기 위한 패키지
implementation 'org.springframework.session:spring-session-data-redis'
@EnableRedisHttpSession // 세션 스토리지를 Redis로 사용하겠다라는 설정
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
1. application.yml 에
spring 아랫단에 추가해줍니다.
spring:
session:
storage-type: redis
redis:
host: localhost
port: 6379
저는 세션을 사용 했기 때문에 로그인이 성공시에 세션을 저장해야합니다.
2. LoginSuccessHandler
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
HttpSession httpSession = request.getSession();
// authentication 안에는 User객체가 들어가 있고, 여기서 getName은 email 을 의미한다.
httpSession.setAttribute("email", authentication.getName());
response.sendRedirect("/");
}
}
2.RedisConfig
@Configuration
public class RedisConfig {
// yml 안에 있는 정보를 프로그램 안으로 가져온다.
@Value("${spring.redis.host}")
public String host;
@Value("${spring.redis.port}")
public int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
레디스를 사용하기 위한 Redis 연결 설정과
RedisTemplate를 성정하는 두 개 의 메서드가 있습니다. 간단하게 설명을 해보겠습니다.
RedicConnectionFactory 는 Redis와 서버와의 연결을 설정하고 제공하는 인터페이스입니다
객체를 생성하여 포트와 호스트를 설정한 후 빈을 생성하여 스프링 컨테이너에 저장합니다.
두 번째 메소드는 Redis의 저장되는 키와 값을 JSON직렬화 방식으로 설정하는 것입니다.
3. LoginService
Service
public class LoginService implements UserDetailsService {
private final AuthorService authorService;
public LoginService(AuthorService authorService) {
this.authorService = authorService;
}
// 내부적으로 로그인시 유저의 정보를 세션에 저장하기 위해 필요한 코드.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Author author = authorService.findByEmail(username);
// 매개변수 : userEmail, password, 권한(authorities)
// 권한이 여러개있을수도 있어서 []
List<GrantedAuthority> authorities = new ArrayList<>();
// Role_권한 패턴으로 스프링에서 기본적으로 권한 체크 ex)ROLE_ADMIN
authorities.add(new SimpleGrantedAuthority("ROLE_"+author.getRole()));
// 해당 메서드에서 return 되는 User객체는 Session 메모리에 저장소에 저장되어, 계속 사용 가능
return new User(author.getEmail(), author.getPassword(),authorities);
}
}
로그인 성공시 User객체 생성 하기 위한 코드입니다.
User객체안에는 이메일,비밀번호,권한(리스트) 로 저장합니다.
"ROLE_ " 를 추가해주는 의미는 스프링은 자체적으로 ROLE_ 을 붙여서 이해하기 떄문입니다. 그래서 저걸 붙여줘야
ROLE_ADMIN 이렇게 됩니다.
로그인 페이지
<div class="container">
<div class="page-header">
<h1>로그인</h1>
</div>
<div id="update-form" class="mt-4">
<form action="/doLogin" method="POST">
<div class="form-group">
<label for="email">email: </label>
<input name="email" class="form-control" type="text" id="email">
</div>
<div class="form-group">
<label for="password">비번: </label>
<input name="pw" class="form-control" type="password" id="password">
</div>
<button class="btn btn-primary" type="submit" value="로그인">로그인</button>
</form>
</div>
</div>
로그인 절차와 검증은 다음과 같습니다.
1. id/pw 를 입력하여 로그인 시도
여기서는 ( email , pw )
저걸로 doLogin을 호출하면 스프링 내부적인 메서드(doLogin) 으로 로그인 시도합니다
.loginProcessingUrl("/doLogin")
.usernameParameter("email")
.passwordParameter("pw")
.successHandler(new LoginSuccessHandler())
2. LoginService 에서 loadUserByUsername에서 db를 조회한 User객체 생성합니다.
3. 로그인 성공시 Authentication객체에 사용자정보(email, role등)을 담고, 사용자에게 SESSIONID를 전달합니다.
{JSESSIONID=7D93988F492D21A0D5FC24E37AFFE56A : USER(email,password,Role)}
로그인 성공시 :
- 키값 발급 → 사용자 정보를 메모리에 저장 (Authentication, 세션저장소) 사용자에게 키값을 전달
- 사용자 정보를 메모리에 저장 (세션저장소)
회원 조회, 게시글 조회 → 쿠키(세션 id) 를 서버에 request값으로 함께 전달
이제 한번 해볼까요?
그냥 레디스에서 키를 뽑아볼게요
키가 없습니다. 로그인을 안 했거든요
3번님 환영합니다 !
레디스에 들어왔는지 확인해볼까요 ?
너무 많아요..
여러 정보가 들어있나봐요
로그아웃하니 2개가 날라가네요. 확인 해 보니 시간이 지나면 사라집니다.
cf)
세션방식 vs 토큰방식
가장 큰 차이점 :
- 세션은 프로그램이 꺼지기 전에 세션 스토리지(메모리) 어디에 저장한다.
- 토큰은 사용자의 인증정보를 저장을 안한다.
곧 토큰 버전도 커스텀해서 가져오도록 하겠습니다.
댓글