riyenas0925 / Sejong_Track_Management

:school: 세종대학교 트랙관리 프로그램
Apache License 2.0
6 stars 2 forks source link

[BUG] 중복로그인 발생시 처리 #114

Closed kimhanui closed 4 years ago

kimhanui commented 4 years ago

증상

시나리오

원인: maxSessionsPreventsLogin(true)

  true - 기존에 동일한 사용자가 로그인 한 경우에는 로그인이 안된다.
  false - 로그인이 되고 기존 접속된 사용자는 Session이 종료된다. false가 디폴트.

screenshot

image

response

server log

1

Request method 'POST' not supported
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:200)
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:419)
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:365)
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:65)
org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:401)
org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1232)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1015)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712)
org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459)
org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384)
org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequestDispatcher.forward(HeaderWriterFilter.java:143)
org.springframework.security.web.access.AccessDeniedHandlerImpl.handle(AccessDeniedHandlerImpl.java:73)
org.springframework.security.web.access.DelegatingAccessDeniedHandler.handle(DelegatingAccessDeniedHandler.java:73)
org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:118)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:748)

실행 환경

kimhanui commented 4 years ago

[BUG-2] 세션이 종료된 결과로 expiredUrl이 아닌 invalidSessionUrl이 호출됨

http.sessionManagement()
                        .invalidSessionUrl("/loginView")
                        .expiredUrl("/memberExpired")

현재 : 다른 지역에서 로그인, 세션만료 시 /loginView 호출

[Solved]

사실 잘 생각해보면....

세션 만료 후 expiredUrl이나 invalidSessionUrl이 없어도 알아서 loginView가 나타남.
세션만료는 어떤 요청이 있을 때 반영돼서 나타날건데,

  1. 그 요청이 권한이 필요한 요청이면 antMatchers 때문에 나타날거고
  2. home처럼 모두가 접근 가능한 요청이면 그냥 로그아웃된채로 뷰가 나타날 것임.

    그래서 invalidSessionUrl 필요없음.

중복 로그인 방지

중복 로그인은 expiredUrl만 있으면 됨.
invalidSessionUrl은 없어도 됨. 정확힌 둘이 같이 쓰면 안되고 expiredUrl만 필요.

  1. 둘다 쓰면 invalid만 수행.

  2. .invalidSessionUrl("/loginView") 쓰면 a 로그인 후 b로그인한다 가정했을 때

a에서 세션이 해제되고 invalid,,,,메소드가 수행된다. -> 로그인 뷰 호출됨.

  1. .expiredUrl("/memberExpired") 쓰면 a에서 세션 해제되고 expired.jsp 호출됨.

그럼 invalid,,, 에 /memberExpired쓰면 되지 않냐? 할 수 있지만 세션이 해제되는 모든 상황에서 당신, expired되었다라는 뷰를 띄울 순 없음.

🔔 3줄요약

  1. 세션만료됐다는 의미의 로그인뷰 띄우기는 세션유지기간 set + antMatchers에 권한별 접근제어만 하면 된다.
  2. 중복 로그인 방지로 선로그인 유저의 접근은 expiredUrl만 쓴다.
  3. invalidSessionUrl은 모든 세션만료 제어용, expiredUrl과 비교했을 때 우선순위가 높은 것같다.