Spring Security Session Destroy

Posted at 2013. 10. 27. 12:39 | Posted in Framework/Spring Security

Intoduction


Spring Security 사용 중에 "사용자의 로그인 시간과 로그아웃 시간을 기록해야 한다" 라는 임무가 떨어졌다 -_-


문제는 이 로그아웃 이라는게 약간 골치 아프다.. 사용자가 직접 로그아웃 버튼을 클릭해서 로그아웃을 한다면 나이스 하지만...


대부분은 용무가 끝나면 그냥 브라우저를 끄거다 바로 다른 사이트로 넘어가게 된다. 이 경우에는 어떻게 하지?


개발자의 눈으로 보면 로그아웃 == 세션 만료로 볼 수 있다.


다른 사이트로 가거나 브라우저를 꺼버리게 되면 WAS의 기준으로 일정 시간이 지나세션이 만료되게 된다.


이 세션 만료를 캐취해서 처리를 하는 방법을 알아보자.




Using HttpSessionListener


서블릿의 세션 리스너를 이용한 방법이다.


아래와 같이 HttpSessionListener 인터페이스를 구현한 클래스를 만들고 web.xml 에 등록하면 된다.


스프링 시큐리티를 사용하지 않는다면 아래의 방법으로 처리하면 된다.


java


package session.destory.servlet;

import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.context.support.WebApplicationContextUtils;

import session.destory.entity.LoginHistory;
import session.destory.security.LoginToken;
import session.destory.service.LoginHistoryService;

public class SessionManagerListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// nothing
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// session
		HttpSession session = se.getSession();
		// spring web application context
		ApplicationContext context = WebApplicationContextUtils
			.getWebApplicationContext(se.getSession().getServletContext());
		// security context
		SecurityContext sc = (SecurityContext) session
			.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
		if (sc != null) {
			// authentication
			Authentication auth = sc.getAuthentication();
			// login history token
			LoginToken loginToken = (LoginToken) auth.getPrincipal();
			LoginHistory lh = loginToken.getHistory();
			lh.setLogoutDate(new Date());
			// update
			LoginHistoryService loginHistoryService = context
				.getBean(LoginHistoryService.class);
			loginHistoryService.modify(lh);
		}

	}

}


web.xml


<!-- servlet listener --> <listener> <listener-class>session.destory.servlet.SessionManagerListener</listener-class> </listener>


리스너에서 수동적으로 스프링 컨택스트와 시큐리티 컨텍스를 꺼내는게 맘에 안드는군... -_-/




Using HttpSessionEventPublisher


이번에는 스프링 시큐리티에서 지원하는 방법으로 해보겠다~!


ApplicationListener<SessionDestroyedEvent> 인터페이스를 구현한 클래스를 만들고 web.xml 에는 HttpSessionEventPublisher 클래스를 등록한다.


java


public class SessionDestoryListener implements ApplicationListener<SessionDestroyedEvent> {

	private LoginHistoryService loginHistoryService;

	@Autowired
	public void setLoginHistoryService(LoginHistoryService loginHistoryService) {
		this.loginHistoryService = loginHistoryService;
	}

	@Override
	public void onApplicationEvent(SessionDestroyedEvent event) {

		List<SecurityContext> contexts = event.getSecurityContexts();
		if (contexts.isEmpty() == false) {
			for (SecurityContext ctx : contexts) {
				Authentication atuh = ctx.getAuthentication().getPrincipal();
				// ...
			}
		}

	}

}

 

spring.xml

 

<beans ...>
	<bean id="sessionDestoryListener" class="session.destory.security.SessionDestoryListener" />
</beans>

 

web.xml


<!-- spring security event -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>


뭔가 스프링스러워진 것 같다 -_-v


- 로그인 처리



- 로그아웃 처리






아래는 샘플 소스다. Tomcat 7, WebLogic 12c 에서 테스트 해봄.


war - 실행해 볼 수 있는 war 파일. 7-zip 으로 분할 압축함.


session.destory.zip.001


session.destory.zip.002



src.zip - Maven 구조로 만들어진 소스 압축함.


session.destory.src.zip


 

... 소스 만들고 나니까 오타가 있네.... Destory → Destroy


'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 궁금합니다!!
    안녕하세요
    세션 만료시 로그아웃 시간 입력을 위해 소스를 참고 했는데요,
    잘 안되는 부분이 있어서 질문좀 드리려고 합니다. ^^;
    spring security / spring 프레임웍을 사용중입니다.
    처음에 ApplicationListener<SessionDestroyedEvent>를 사용해서 구현했는데 SecurityContext 리스트가 계속 null이더라구요.
    결국 세션에서 얻어오는 event.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
    방법으로 security context를 얻어왔습니다.
    궁금한게 getSecurityContexts() 로 리스트를 얻어올때는 null 이었는데 왜 세션에서 얻어오는 방법으로는 얻을수 있는지 모르겠습니다. 혹시 참고할만한 부분이나 조언을 해주시면 감사하겠습니다!
    • 2014.04.03 14:17 신고 [Edit/Del]
      1. 혹시 web.xml 에 리스너 등록 하셨죠?
      <listener>
      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
      </listener>

      2. 잠깐 찾아보니 스프링 시큐리티 버전에 따른 문제가 있는 것 같기도 합니다. (아닐지도 모름 -_-ㄷㄷ)

      3. 저도 퇴근해서 이것저것 해보겠습니다.
  2. 궁금합니다!!
    넵 감사합니다!
    리스너는 등록한 상태구요, 사용중인 spring security 버전은 3.1.0입니다.
    getSecurityContexts()가 왜 계속 null인지.. 계속 다시 해봤는데 모르겠네요
    getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) 과 내부적으로 같은 거라면 그냥 쓰려고 합니다.
    블로그 올려두신 내용들 많이 참고했습니다!! 감사합니다!

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기