본문으로 건너뛰기

설정방법

애플리케이션에서 사용 방법

Infinispan HotRod 모드

  • pom.xml에 dependency 추가
<dependency>
<groupId>com.opennaru.khan</groupId>
<artifactId>khan-session-hotrod</artifactId>
<version>5.1.0</version>
</dependency>
  • 필터 클래스명

com.opennaru.khan.session.filter.InfinispanHotRodSessionFilter

애플리케이션 설정 방법

web.xml의 필터 설정 추가

<?xml version="1.0" encoding="UTF-8"?>
{/*
~ Opennaru, Inc. http://www.opennaru.com/
~
~ Copyright (C) 2014 Opennaru, Inc. and/or its affiliates.
~ All rights reserved by Opennaru, Inc.
*/}
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="session1" version="2.5">

<display-name>Test</display-name>
<description>Test App</description>
{/* <distributable/> */}
<filter>
<filter-name>KhanSessionFilter</filter-name>
{/* Hotrod Mode */}
<init-param>
<param-name>configFile</param-name>
<param-value>${OPENMARU_CONFIG_FILE:hotrod.properties}</param-value>
</init-param>
<init-param>
<param-name>infinispanCache</param-name>
<param-value>${OPENMARU_INFINISPAN_CACHE:OPENMARU_SESSION}</param-value>
</init-param>
<init-param>
<param-name>infinispanLoginCache</param-name>
<param-value>${OPENMARU_INFINISPAN_LOGIN_CACHE:OPENMARU_SESSION_LOGIN}</param-value>
</init-param>
<init-param>
<param-name>sessionId</param-name>
<param-value>__KSMSID__</param-value>
</init-param>
<init-param>
<param-name>domain</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>path</param-name>
<param-value>/test1</param-value> {/* 서로 다른 WebApp간 세션 공유하려면 '/' 으로 설정 */}
</init-param>
<init-param>
<param-name>secure</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>httpOnly</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>sessionTimeout</param-name>
<param-value>30</param-value>
</init-param>
<init-param>
<param-name>excludeRegExp</param-name>
<param-value>/.+\.(html|jpg|jpeg|png|gif|js|css|swf)</param-value>
</init-param>
<init-param>
<param-name>allowDuplicateLogin</param-name> {/* 중복 로그인을 허용하려면 true로 설정 */}
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>duplicateLoginPolicy</param-name> {/* 중복 로그인 정책: none, legacy, custom */}
<param-value>legacy</param-value>
</init-param>
<init-param>
<param-name>invalidateDuplicateLogin</param-name> {/* false 로 설정 하면 내부적으로 invalidate를 호출하지 않음(중복 여부 API 로 체크 후 직접 invalidate API 호출) */}
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>logoutUrl</param-name> {/* 중복 로그인시 logout URL 설정 */}
<param-value>/logout.jsp</param-value>
</init-param>
<init-param>
<param-name>enableImmediateSave</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>KhanSessionFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<listener>
<listener-class>com.opennaru.khan.session.listener.SessionListener</listener-class>
</listener>

SpringBoot 사용시 설정방법

SpringBoot에서 XML을 사용하지 않고 Java 코드 기반으로 설정하는 환경에서는 아래와 같이 필터 설정 코드를 추가하여 설정할 수 있다.

@Configuration
public class OpenmaruFilterConfiguration implements WebMvcConfigurer {

@Bean
public FilterRegistrationBean getFilterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new InfinispanHotRodSessionFilter());
registrationBean.setOrder(Integer.MIN_VALUE);

registrationBean.addInitParameter(Constants.INFINISPAN_CONFIGFILE_KEY, "hotrod.properties");
registrationBean.addInitParameter(Constants.INFINISPAN_CACHE_KEY, "KHAN_SESSION");
registrationBean.addInitParameter(Constants.INFINISPAN_LOGIN_CACHE_KEY, "KHAN_SESSION_LOGIN");
registrationBean.addInitParameter(Constants.SESSION_ID, "__KSMSID__");
registrationBean.addInitParameter(Constants.DOMAIN, "");
registrationBean.addInitParameter(Constants.PATH, "/");
registrationBean.addInitParameter(Constants.SECURE, "false");
registrationBean.addInitParameter(Constants.HTTP_ONLY, "false");
registrationBean.addInitParameter(Constants.SESSION_TIMEOUT, "30"); // minute
registrationBean.addInitParameter(Constants.SESSION_SAVE_DELAY, "5");
registrationBean.addInitParameter(Constants.EXCLUDE_REG_EXP, "/.+\\.(html|jpg|jpeg|png|gif|js|css|swf)");
registrationBean.addInitParameter(Constants.ALLOW_DUPLICATE_LOGIN, "true");
registrationBean.addInitParameter(Constants.DUPLICATE_LOGIN_POLICY, "legacy"); // none, legacy, custom
registrationBean.addInitParameter(Constants.DUPLICATE_LOGIN_EXCLUSTION_TYPE, "");
registrationBean.addInitParameter(Constants.INVALIDATE_DUPLICATE_LOGIN, "true");
registrationBean.addInitParameter(Constants.LOGOUT_URL, "");
registrationBean.addInitParameter(Constants.ENABLE_IMMEDIATED_SAVE, "true");
registrationBean.addInitParameter(Constants.ENABLE_STATISTICS, "true");
registrationBean.addInitParameter(Constants.ENABLE_MEMORY_STATISTICS, "false");
registrationBean.addInitParameter(Constants.LICENSE_KEY,
"#### LICENSE KEY ###\n" +
"> REQUEST sales@openmaru.io"
);

registrationBean.setUrlPatterns(Arrays.asList("/*"));
registrationBean.setDispatcherTypes(DispatcherType.ERROR, DispatcherType.INCLUDE, DispatcherType.FORWARD, DispatcherType.REQUEST);
return registrationBean;
}

@Bean
public HttpSessionListener httpSessionListener() {
SessionListener sessionListener = new SessionListener();
System.out.println("SessionListener started.");
return sessionListener;
}
}

키는 순서대로 xml, 환경변수, jvm 속성 설정 시 값

설명기본값
configFile
KSM_CONFIG_FILE
ksm.config.file
설정파일
JVM 환경변수: OPENMARU_CONFIG_FILE
hotrod.properties
infinispanCache
KSM_INFINISPAN_CACHE
ksm.infinispan.cache
세션 메타 저장소OPENMARU_SESSION
infinispanLoginCache
KSM_INFINISPPAN_LOGIN_CACHE
ksm.infinispan.login.cache
세션 데이터 저장소OPENMARU_SESSION_LOGIN
sessionId
KSM_SESSION_ID
ksm.session.id
세션 키(JSESSIONID를 대체하여 사용)KSMSID
domain
KSM_DOMAIN
ksm.domain
세션을 공유할 도메인
멀티 도메인의 경우 subdomain만 다른 경우 공유 가능
ex) login.openmaru.io, www.openmaru.io
path
KSM_PATH
ksm.path
공유할 Context
서로 다른 WebApp간 세션 공유하려면 '/' 으로 설정
/
secure
KSM_SECURE
ksm.secure
Cookie Secure 옵션false
httpOnly
KSM_HTTP_ONLY
ksm.http.only
Cookie httpOnly 옵션false
sameSite
KSM_SAME_SITE
ksm.same.site
Cookie sameSite 옵션
sessionTimeout
KSM_SESSION_TIMEOUT
ksm.session.timeout
세션 타임아웃(분)30
excludeRegExp
KSM_EXCLUDE_REG_EXP
ksm.exclude.reg.exp
세션 호출 제외 확장자/.+\.(html|jpg|jpeg|png|gif|js|css|swf)
allowDuplicateLogin
KSM_ALLOW_DUPLICATE_LOGIN
ksm.allow.duplicate.login
중복 로그인을 허용하려면 true로 설정
ex) SessionLoginManager.getInstance().login(request, [ID])
true
duplicateLoginPolicy
KSM_DUPLICATE_LOGIN_POLICY
ksm.duplicate.login.policy
중복 로그인 정책 설정
none: 아무런 동작 안함
legacy: 이전 DUPLICATED 정보 리턴으로 관리(이전에 로그인된 사용자 logout)
custom: 중복 로그인된 session list로 관리(이전, 이후 모두 가능)
legacy
invalidateDuplicateLogin
KSM_INVALIDATE_DUPLICATE_LOGIN
ksm.invalidate.duplicate.login
중복 로그인 감지 시 기존 세션 무효화 여부
true: 중복 로그인 감지 시 기존 세션을 자동으로 무효화
false: 중복 로그인 감지 시 세션을 유지하고 로그아웃 URL로만 리다이렉트
false
duplicateLoginExclustionType
KSM_DUPLICATE_LOGIN_EXCLUTION_TYPE
ksm.duplicate.login.exclution.type
중복로그인 제외 사용자 유형
여러개 일 경우 콤마로 구분
ex) SessionLoginManager.getInstance().login(request, [ID], [TYPE])
logoutUrl
KSM_LOGOUT_URL
ksm.logout.url
중복 로그인 감지 시 리다이렉트할 로그아웃 URL
• 설정된 경우: 중복 로그인 감지 시 해당 URL로 리다이렉트
• 빈 문자열인 경우: 리다이렉트하지 않음
(빈 문자열)
enableImmediateSave
KSM_ENABLE_IMMEDIATE_SAVE
ksm.enable.immediate.save
enableImmediateSave값을 true로 설정하면 setAttribute()가 호출될때 즉시 스토어에 저장함false

Hotrod 클라이언트/서버 모드를 사용할 때 설정

hotrod.properties 파일에서 Infinispan 서버의 IP:PORT 설정한다. JBoss Data Grid(Infinispan) Server는 OPENMARU Installer를 이용하여 자동 설치할 수 있습니다.

infinispan.client.hotrod.server_list = 192.168.0.11:11222

메모리 모니터링을 위한 설정

JBoss EAP 6.x는 아래 설정이 필요함(다른 WAS는 필요 없음)

export JAVA_OPTS=" $JAVA_OPTS -Djboss.modules.system.pkgs=org.jboss.byteman,org.github.jamm"

javaagent 옵션 설정

export JAVA_OPTS=" $JAVA_OPTS -javaagent:/PATH_TO_JAMM_JAR/jamm-0.2.5.jar"

메모리 모니터링 활성화

web.xml의 필터 설정에서 메모리 모니터링을 활성화한다.

<init-param>
<param-name>enableMemoryStatistics</param-name>
<param-value>true</param-value>
</init-param>

중복 로그인 방지 기능 설정

  • Cluster 에서 제공하는 중복 로그인 방지 기능을 사용하기 위해서는 옵션 설정과 소스 코드에 추가하는 부분이 필요하다.

설정

web.xml

<init-param>
<param-name>allowDuplicateLogin</param-name>
<param-value>false</param-value> {/* false 로 설정해야 중복 로그인 방지 기능 활성화 */}
</init-param>
<init-param>
<param-name>duplicateLoginPolicy</param-name>
<param-value>legacy</param-value> {/* legacy 가 default 이므로 별도로 설정하지 않아도 legacy 모드 */}
</init-param>
<init-param>
<param-name>invalidateDuplicateLogin</param-name>
<param-value>true</param-value> {/* true 로 설정해야 중복 로그인 감지 시 기존 세션을 자동으로 무효화 */}
</init-param>
<init-param>
<param-name>logoutUrl</param-name>
<param-value>/logout.jsp</param-value> {/* 중복 로그인 시 logout url 설정 */}
</init-param>

OpenmaruFilterConfiguration

@Configuration
public class OpenmaruFilterConfiguration implements WebMvcConfigurer {

@Bean
public FilterRegistrationBean getFilterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new InfinispanHotRodSessionFilter());
registrationBean.setOrder(Integer.MIN_VALUE);

...
// false 로 설정해야 중복 로그인 방지 기능 활성화
registrationBean.addInitParameter(Constants.ALLOW_DUPLICATE_LOGIN, "false");

// legacy 가 default 이므로 별도로 설정하지 않아도 legacy 모드
registrationBean.addInitParameter(Constants.DUPLICATE_LOGIN_POLICY, "legacy");

// true 로 설정해야 중복 로그인 감지 시 기존 세션을 자동으로 무효화
registrationBean.addInitParameter(Constants.INVALIDATE_DUPLICATE_LOGIN, "true");

// 중복 로그인 시 logout url 설정
registrationBean.addInitParameter(Constants.LOGOUT_URL, "/logout.jsp");
...

registrationBean.setUrlPatterns(Arrays.asList("/*"));
registrationBean.setDispatcherTypes(DispatcherType.ERROR, DispatcherType.INCLUDE, DispatcherType.FORWARD, DispatcherType.REQUEST);
return registrationBean;
}
...
}

소스 코드 추가

  • 중복로그인 방지 기능을 사용하려면 로그인, 로그아웃시 OPENMARU CLUSTER가 제공하는 API를 사용하여 소스 코드를 추가하여야 한다.
  • 로그인 성공 후 그리고 invalidateDuplicateLogin 값이 false 일 경우에는 로그아웃 및 세션 무효화 코드를 추가하여야 한다.

로그인 성공 후

  • 아래 소스코드 추가
SessionLoginManager.getInstance().login(request, [USER_ID]);

로그아웃 및 세션 무효화

invalidateDuplicateLogin=true 인 경우 (자동 로그아웃 처리)

invalidateDuplicateLogintrue로 설정되어 있으면 중복 로그인 시 자동으로 로그아웃 처리를 수행한다.

동작

1. 중복 로그인 감지 시 `SessionLoginManager.getInstance().logout()` 자동 호출
2. 세션 무효화(`session.invalidate()`)까지 자동 처리
3. 설정된 로그아웃 URL로 자동 리다이렉트 (logoutUrl 설정 시)

따라서 애플리케이션 코드에서 별도의 로그아웃 처리 코드를 작성할 필요가 없다.

invalidateDuplicateLogin=false 인 경우 (수동 로그아웃 처리)

invalidateDuplicateLoginfalse로 설정하면 중복 로그인이 감지되어도 시스템에서 자동으로 세션을 무효화하지 않는다. 중복 로그인을 직접 체크 및 invalidate 해야 할 경우 활용할 수 있다.

동작

1. 중복 로그인 감지 시 기존 세션을 무효화하지 않음
2. 세션 정보를 세션 저장소에서 제거하지 않음 (loginRemove 호출 안함)
3. SessionLoginManager.logout() 호출하지 않음
4. session.invalidate() 호출하지 않음
5. 설정된 logoutUrl로 리다이렉트만 수행 (logoutUrl이 설정된 경우)

수동으로 로그아웃 처리를 해야 하는 경우 다음 코드를 사용한다.

SessionLoginManager.getInstance().logout(request);

사용자 명시적 로그아웃 처리

사용자가 직접 로그아웃 버튼을 클릭하는 경우 다음과 같이 처리한다.

// 로그아웃 처리 예제
public void doLogout(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
// OPENMARU Cluster 로그아웃 처리
SessionLoginManager.getInstance().logout(request);

// 세션 무효화
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}

// 로그인 페이지로 리다이렉트
response.sendRedirect("/login.jsp");

} catch (Exception e) {
// 로그아웃 실패 시 에러 처리
log.error("로그아웃 처리 중 오류 발생", e);
throw e;
}
}
  • 사용자 명시적 로그아웃 시에는 반드시 SessionLoginManager.getInstance().logout(request) 호출 후 session.invalidate()를 호출해야 한다.
  • invalidateDuplicateLogin=true로 설정하면 중복 로그인 시 자동 처리되므로 별도 코드가 필요하지 않다.

활용 API

중복 로그인 체크

Java 코드에서 중복로그인 여부를 isDuplicated 메소드로 확인할 수 있다.

boolean isDuplicated = SessionLoginManager.getInstance().isDuplicated(request);

로그인한 사용자 정보

Java 코드에서 loggedInUserId 메소드로 로그인한 사용자 ID를 얻을 수 있다.

String loginUserId = SessionLoginManager.getInstance().loggedInUserId(request);

로그인 사용자 리스트

Java 코드에서 로그인한 사용자 리스트를 얻을 수 있다.

List<LoginUser> loginUsers = SessionLoginManager.getInstance().getLoginUsers();

로그인 사용자 Session Attributes

로그인한 사용자의 세션 속성을 getSessionAttributes API로 가져올 수 있다.

Map<String, Object> sessionAttributes = SessionLoginManager.getInstance().getSessionAttributes(sessionId);

특정 로그인 사용자 강제 로그아웃

logout API를 이용하면 특정 사용자를 강제 로그아웃 시킬 수 있다. 이때, 데이터는 삭제하지 않고 중복 로그인이 됐다고 처리한다.

SessionLoginManager.getInstance().logout(loginId, sessionId);

세션 서버에 저장된 데이터 조회

// 세션 ID에 대한 “name” 속성이 Null이 아니면 true를 반환
public boolean isAttributeExistDirect(String sessionId, String
name)
// 세션 ID에 대한 “name” 속성 값을 Object로 반환
public Object getAttributeDirect(String sessionId, String name)

SessionLoginManager.getInstance().getAttributeDirect(session.getId(),
"userId");
SessionLoginManager.getInstance().isAttributeExistDirect(session.getId(),
"userId");

직렬화(Serializable) 필요

세션에 저장하는 객체는 반드시 직렬화되어 있어야 한다.

아래와 같이 User 객체를 세션에 저장하려면, User 클래스는 Serializable을 구현한 객체이어야 한다.

  User user = new User();
...
session.setAttribute("key", user);
public class User implements Serializable {

}

모니터링 활성화 방법

web.xml의 필터 설정에서 MBean 모니터링을 활성화한다.

<init-param>
<param-name>enableStatistics</param-name>
<param-value>true</param-value>
</init-param>