오늘은 로그인, 로그아웃, 권한 설정 기능을 만들고 있다. 일단 로그인과 권한 설정까지 완료했는데 로그아웃이 문제다..
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true) //동시 로그인 차단, false인 경우 기존 세션 만료(default)
)
내가 원하는 설정은 넷상에 존재하는 다른 홈페이지들 처럼 클라이언트에서 1개의 세션만이 로그인 되도록 하는 것이기 때문에 위처럼 최대 세션수를 1개로 정해놓고 동시 로그인을 차단시켜놨다.
원인은 세션이 종료된걸 서버에서 모르는 듯 한데... 근데 그러기엔 세션이 존재해야만 보이는 마이페이지 버튼도 안보였다.. 일단 세션이 끊겼는지 아닌지 제대로 한번 알아보기 위해서 log를 찍어보았다
anonymousUser 를 보면 세션이 끊겼음에도 불구하고 계속 에러가 뜨는 것이었다..
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) //csrf 비활성화
.authorizeHttpRequests((requests) -> requests
// 권한 없이 접근 가능한 URL 경로 설정
.requestMatchers("/accounts", "/", "/register","/login").permitAll()
.requestMatchers("/mypage").hasRole("ADMIN") // ADMIN 권한이 필요한 경로
.anyRequest().authenticated()
)
// 로그인 폼 설정 (기본 제공 로그인 페이지 사용)
.formLogin((form) -> form
.permitAll()
.defaultSuccessUrl("/", true)
)
//최대 세션수 1개로 설정
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true) //동시 로그인 차단, false인 경우 기존 세션 만료(default)
.sessionRegistry(sessionRegistry()) // 세션 레지스트리 설정
)
// 로그아웃 설정
.logout((logout) -> logout
.logoutUrl("/logout") // 로그아웃 URL
.logoutSuccessUrl("/login") // 로그아웃 후 리디렉션 URL
.invalidateHttpSession(true) // HTTP 세션 무효화
.deleteCookies("remember-me")
.deleteCookies("JSESSIONID") // 삭제할 쿠키 설정 (기본적으로 "JSESSIONID" 사용)
);
return http.build();
}
해결책
1. remeber-me 쿠키삭제 => 안됨
2. JSESSIONID 쿠키삭제 => 안됨
3. invalidateHttpSession(true) ==> 안됨
이렇게 구글링한 해결책을 적용시켜봤지만 전혀 소용이 없었다. maximumSessions 수를 더 추가하면 문제가 바로 해결되지만 그런식으로 해결하긴 싫었다..
그래서 구글링 하던 도중 해결방법을 찾았다!
해결방법
@Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true) //동시 로그인 차단, false인 경우 기존 세션 만료(default)
.sessionRegistry(sessionRegistry()) // 세션 레지스트리 설정
)
sessionManagement 에 sessionRegistry를 연결시키고 @Bean ServletListenerRegistrationBean 을 추가시켜주면 해결된다!
하지만 여기서 끝낼게 아니다.. ServletListenerRegistrationBean 이 무슨역할을 하는지 공부를 해봤더니 가장 핵심적인 역할을 했던 객체는 HttpSessionListener 였다. 이걸 공부하기전에 리스너라는 개념을 먼저 챙겨보자
리스너란?
리스너는 어떤 이벤트가 발생하길 기다리다가 실행되는 컴포넌트를 말한다. 리스너는 이벤트가 발생함과 동시에 특정 행동을(메서드나 함수를 실행)하는데, 이것을 이벤트 핸들링이라고 합니다. 따라서 리스너를 이벤트 핸들러라고 부르기도 합니다.
Servlet/JSP의 리스너와 같이 웹어플리케이션의 시작이나, 종료, 특정 객체의 생성, 소멸과 같은것도 이벤트라고 할 수 있습니다. Java에서 리스너는 객체가 되며, 특정 이벤트가 발생했을때 실행되는(이벤트를 처리할) 메서드를 가지고 있습니다.
HttpSessionListener
HttpSessionEventPublisher 클래스에 implements 되어있는 HttpSessionListener는 Session이 생성되거나 제거될때 발생하는 이벤트를 제공하므로 등록만 해주면 세션을 통제할 수 있다.
그럼 결국 ServletListenerRegistrationBean은 리스너를 서블릿에 등록해주기 위한 객체였고 그 안에 있는 HttpSessionEventPublisher 에 있는 HttpSessionListener가 제대로 세션을 처리해주는 역할을 했던 것이었다.세션을 끊는 메서드를 사용하여도 제대로 명령을 수행할 수 없는 것을 처음 알았다.. 개발을 빠르게 하고싶어서 Spring Boot를 사용하지만 탄탄한 세부적인 설정을 위해서라면 Spring 프레임 워크를 사용하는 게 더 좋은 선택 같기도하다.. 통제를 더 잘하기위해 이벤트 리스너를 서블릿에 등록해줘야한다는 교훈을 얻게 되었다
-리스너 개념 배운곳
출처: https://dololak.tistory.com/616 [코끼리를 냉장고에 넣는 방법:티스토리]
- 해결방법 얻는 곳
출처:https://gompangs.tistory.com/entry/Spring-Boot-Spring-Security-maximumSessions-%EA%B4%80%EB%A0%A8
- maximumSessions(-1)
- 설명: 이 설정은 사용자가 동시에 가질 수 있는 세션의 최대 수를 정의합니다. -1로 설정하면 무제한 세션을 허용합니다. 따라서 이 설정만으로는 세션 수에 제한이 없습니다.
- maxSessionsPreventsLogin(true)
- 설명: 이 설정은 사용자가 동시에 최대 허용 세션 수를 초과하는 로그인 시도를 할 때, 추가 로그인 시도를 차단하는지 여부를 정의합니다. 이 설정을 true로 설정하면, 사용자가 최대 허용 세션 수를 초과하려고 할 때 추가 로그인이 차단됩니다. 기존 세션은 유지됩니다.
'Back-End > Spring' 카테고리의 다른 글
[Spring] RestAPI와 @ResponseEntity (0) | 2024.09.10 |
---|---|
[Spring Security] spring security 에서 CSRF 작동시키는 이유 (2) | 2024.09.09 |
[Spring] @Autowired 와 @RequiredArgsConstructor (1) | 2024.09.06 |
[Spring] 의존성 주입이란? (1) | 2024.09.06 |