Login

Language :
TitleSpring, Redis Session 연동하기 - 중복 세션 관리
Spring, Redis Session - Duplicate Session Management
Writer이지섭Write DateJul 12 2020Modify DateJul 3 2024View Count10506

The idea is to use Redis Session in Spring to manage redundant sessions.

 

Below is the Maven pom.xml dependency settings.

  <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>2.3.3.RELEASE</version>
  </dependency>
 
  <dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
      <version>5.3.1.RELEASE</version>
  </dependency>
 
  <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.3.9.RELEASE</version>
  </dependency>
 
  <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-core</artifactId>
      <version>2.3.3.RELEASE</version>
  </dependency>

  <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-commons</artifactId>
      <version>2.3.9.RELEASE</version>
  </dependency>
        
  <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-keyvalue</artifactId>
      <version>2.3.9.RELEASE</version>
  </dependency>

 

Below is also a necessary library in some cases, so I wrote it together.

  <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-transport</artifactId>
      <version>4.1.51.Final</version>
  </dependency>

  <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-handler</artifactId>
      <version>4.1.51.Final</version>
  </dependency>

  <dependency>
      <groupId>io.reactivex</groupId>
      <artifactId>rxjava</artifactId>
      <version>1.0.17</version>
  </dependency>

  <dependency>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-core</artifactId>
      <version>3.4.14</version>
  </dependency>

  <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-codec</artifactId>
      <version>4.0.33.Final</version>
  </dependency>

  <dependency>
      <groupId>io.reactivex.rxjava3</groupId>
      <artifactId>rxjava</artifactId>
      <version>3.0.9</version>
  </dependency>

 

Below is the setting in web.xml.

springSessionRepositoryFilter is the first of the filters.

And SpringSecurityFilterChain is the next.

The order seemed important.

The pages referenced in the order are as follows.

https://stackoverflow.com/questions/35496089/spring-session-data-redis-not-working

Then, <distributable /> is set.

  <!-- redis -->
<!--
  <filter>   <filter-name>springSessionRepositoryFilter</filter-name>   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  </filter>   <filter-mapping>   <filter-name>springSessionRepositoryFilter</filter-name>   <url-pattern>/*</url-pattern>   </filter-mapping> -->
<!-- <filter>   <filter-name>springSecurityFilterChain</filter-name>   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  </filter>   <filter-mapping>   <filter-name>springSecurityFilterChain</filter-name>   <url-pattern>/*</url-pattern>   </filter-mapping>  -->
  ....   <distributable />

 

Below is the jboss-web.xml file used in JBoss. Located in the WEB-INF folder.

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
 
  <context-root>/</context-root>

  <replication-config>
    <replication-trigger>SET</replication-trigger>
    <replication-granularity>SESSION</replication-granularity>
  </replication-config>
 
</jboss-web>

 

Below is a Java class implementation of the Redis setting of Spring.

@Configuration
//@EnableRedisHttpSession(maxInactiveIntervalInSeconds=32400)
@PropertySource("classpath:application.properties")
public class RedisConfigureAction {

    @Value("${redis.host}")
    private String redisHostName;

    @Value("${redis.port}")
    private int redisPort;
    
    @Value("${redis.password}")
    private String redisPwd;
    
    // lettuce
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {

        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHostName, redisPort);
        redisStandaloneConfiguration.setPassword(redisPwd);

        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration);

        return lettuceConnectionFactory;
    }

    @Bean
    public StringRedisTemplate redisTemplate() {
        StringRedisTemplate redisTemplate = new StringRedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }
}

 

Below is the application.properties file.

The content will have to be personalized.

In some cases, the Redis password is not used.

redis.host=localhost
redis.port=6379
redis.password=123456
redis.maxInactiveIntervalInSeconds=50000

 

Below is the contents of the spring-common-context.xml file for using the contents of applications.properties in the xml setup.

Set the application properties.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
      http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

 <util:properties id="application" location="classpath:application.properties" />

  ......

</beans>

 

Below is the contents of the spring-redis-context.xml file.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
 xmlns:cache="http://www.springframework.org/schema/cache"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!-- <context:annotation-config/> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <property name="maxInactiveIntervalInSeconds" value="#{application['redis.maxInactiveIntervalInSeconds']}" /> </bean>
-->
<bean id="lettuceConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" destroy-method="destroy"> <property name="hostName" value="#{application['redis.host']}" /> <property name="port" value="#{application['redis.port']}" /> <property name="password" value="#{application['redis.password']}" /> </bean> <util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="lettuceConnectionFactory" /> <property name="defaultSerializer" ref="defaultRedisSerializer"/> </bean> <bean id="defaultRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean> </beans>

 

In the web.xml file, the filter settings related to the redis session were commented out.

Also in the spring-redis-context.xml file, RedisHttpSessionConfiguration settings were commented out.

 

Duplicate sessions could be managed by entering and reading session data directly into the redis.

 
To make sure the session itself is all stored on redis,
 
1) In your java configuration, add the following
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=32400)
 
If it's an xml configuration, uncomment the following
RedisHttpSessionConfiguration
 
2) In your web.xml file, add the following
  <filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
 
If you don't have a web.xml file, just skip it.

 

Below is a service program that inputs, deletes, and reads data into the redis.

 

package isry.redis.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import isry.itgcms.sysmgmt.userauth.vo.UserInstAuthVO;
import isry.itgcms.sysmgmt.userlogin.vo.UserDetailsVO;

@Component
public class RedisService3 {

	private static final Logger logger = LoggerFactory.getLogger(RedisService3.class);
	
	@Autowired
	StringRedisTemplate redisTemplate;

	public void insertRedisMap(String redisKey, Map<String, Object> map) {

		HashOperations<String, Object, Object> stringObjectObjectHashOperations = redisTemplate.opsForHash();
		
		//Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			//System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
			
			stringObjectObjectHashOperations.put(redisKey, entry.getKey(), entry.getValue());
		}

		//stringObjectObjectHashOperations.put(redisKey, "Hello", "rg1");
		//stringObjectObjectHashOperations.put(redisKey, "Hello2", "rg2");
		//stringObjectObjectHashOperations.put(redisKey, "Hello3", "rg3");
		
	}

	public void processRedisLogout(String redisKey) {
		Set<String> keys = redisTemplate.keys(redisKey + "*");
		for (String key : keys) {
			redisTemplate.delete(key);
		}
	}
	
	public int selectRedisLikeSessionCount(String redisKey) {
		int count = 0;
		Set<String> keys = redisTemplate.keys(redisKey + "*");
		for (String key : keys) {
			count++;
		}
		return count;
	}
	
	public List<UserDetailsVO> selectPrevLoginVOList(String redisKey) {
		
		List<UserDetailsVO> list = new ArrayList<>();
		
		if (redisKey == null || redisKey.trim().equals("")) {
			return null;
		}
		
		Set<String> keys = redisTemplate.keys(redisKey + "*");
		for (String key : keys) {
			UserDetailsVO vo = selectRedisSession(key);
			list.add(vo);
		}
		
		return list;
	}
	
	public void deleteRedisLikeSession(String redisKey) {
		Set<String> keys = redisTemplate.keys(redisKey + "*");
		for (String key : keys) {
			redisTemplate.delete(key);
		}
	}
	
	public void setTimeOutSecond(String key, Integer sessionTime) {
		redisTemplate.expire(key, sessionTime, TimeUnit.SECONDS);
	}
	
	@SuppressWarnings("unchecked")
	public UserDetailsVO selectRedisSession(String key) {
		
		HashOperations<String, Object, Object> stringObjectObjectHashOperations = redisTemplate.opsForHash();
		Map<Object, Object> entries = stringObjectObjectHashOperations.entries(key);
		
		UserDetailsVO vo = new UserDetailsVO();
		vo.setAge(String.valueOf(entries.get("age")));
		vo.setAgencyContacts(String.valueOf(entries.get("agencyContacts")));
		vo.setAuthrtSeCd(String.valueOf(entries.get("authrtSeCd")));
		vo.setBirthdate(String.valueOf(entries.get("birthdate")));
		vo.setCertificate(String.valueOf(entries.get("certificate")));
		vo.setCtpvNm(String.valueOf(entries.get("ctpvNm")));
		vo.setDeptCd(String.valueOf(entries.get("deptCd")));
		vo.setDeptNm(String.valueOf(entries.get("deptNm")));
		vo.setEmail(String.valueOf(entries.get("email")));
		vo.setEnfsnNo(String.valueOf(entries.get("enfsnNo")));
		vo.setEnfsnRoleSeCd(String.valueOf(entries.get("enfsnRoleSeCd")));
		vo.setEngCtpvNm(String.valueOf(entries.get("engCtpvNm")));
		vo.setGender(String.valueOf(entries.get("gender")));
		vo.setGroupAuthrtSeCd(String.valueOf(entries.get("groupAuthrtSeCd")));
		vo.setId(String.valueOf(entries.get("id")));
		vo.setIndvIdntfcNo(String.valueOf(entries.get("indvIdntfcNo")));
		vo.setInstAuthList((List<UserInstAuthVO>)entries.get("instAuthList"));
		vo.setInstNm(String.valueOf(entries.get("instNm")));
		vo.setInstNo(Integer.parseInt(String.valueOf(entries.get("instNo"))));
		vo.setInstTypeSeCd(String.valueOf(entries.get("instTypeSeCd")));
		vo.setIp(String.valueOf(entries.get("ip")));
		vo.setLastLoginTime(String.valueOf(entries.get("lastLoginTime")));
		vo.setLgnScsYn(String.valueOf(entries.get("lgnScsYn")));
		vo.setManagerYn(String.valueOf(entries.get("managerYn")));
		vo.setMemberType(String.valueOf(entries.get("memberType")));
		vo.setMobile(String.valueOf(entries.get("mobile")));
		vo.setOrgCode(Integer.parseInt(String.valueOf(entries.get("orgCode"))));
		vo.setOrgName(String.valueOf(entries.get("orgName")));
		vo.setPass(String.valueOf(entries.get("pass")));
		vo.setRgnSeCd(String.valueOf(entries.get("rgnSeCd")));
		vo.setSessionId(String.valueOf(entries.get("sessionId")));
		vo.setSggCd(String.valueOf(entries.get("sggCd")));
		vo.setSggNm(String.valueOf(entries.get("sggNm")));
		vo.setSidoNm(String.valueOf(entries.get("sidoNm")));
		vo.setSigunguNm(String.valueOf(entries.get("sigunguNm")));
		vo.setTopMenuNo(String.valueOf(entries.get("topMenuNo")));
		vo.setUntTaskwk(String.valueOf(entries.get("untTaskwk")));
		vo.setUntTaskwkSeCd(String.valueOf(entries.get("untTaskwkSeCd")));
		vo.setUserInstNo(Integer.parseInt(String.valueOf(entries.get("userInstNo"))));
		vo.setUserName(String.valueOf(entries.get("userName")));
		vo.setWrdTelno(String.valueOf(entries.get("wrdTelno")));
		vo.setYngbgsPrtcrNo(String.valueOf(entries.get("yngbgsPrtcrNo")));
		
		return vo;
	}
	
	public Set<String> selectKey(String key) {
		Set<String> set = new HashSet<>();
		
		SetOperations<String, String> stringStringSetOperations = redisTemplate.opsForSet();

		Cursor<String> cursor = stringStringSetOperations.scan(key, ScanOptions.scanOptions().match("*").build());

		while (cursor.hasNext()) {
			//logger.debug("cursor = " + cursor.next());
			set.add(cursor.next());
		}
		
		return set;
	}
}

 

Using the above program, you can use the redis to save and read the login session as follows.

 

package com.rg.util.controller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.rg.login.dto.CustomUserDetails;
import com.rg.login.dto.UserDetailsVO;
import com.rg.login.service.LoginService;
import com.rg.util.RedisService3;

import java.util.Optional;

@Controller
public class EnvironmentController {

	private final Logger logger = LogManager.getLogger(EnvironmentController.class);

	@Autowired
	private LoginService loginService;

	@Autowired
	private RedisService3 redisService;
	
	@RequestMapping(value = {"/getEnvironment.do", "/{lang}/getEnvironment.do"})
	@ResponseBody
	public Map getEnvironment(@PathVariable("lang") Optional langVal,
			HttpServletRequest request, HttpServletResponse response) {
		
		HttpSession session = request.getSession();

		UserDetailsVO vo = null;
		
		CustomUserDetails userInfo = null;
		
		String loginId = null;
		String loginUserName = null;
		
		try {
			if (SecurityContextHolder.getContext().getAuthentication() != null &&
				SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
				if (SecurityContextHolder.getContext().getAuthentication().getDetails() instanceof CustomUserDetails) {
					userInfo = (CustomUserDetails)SecurityContextHolder.getContext().getAuthentication().getDetails();
					loginId = userInfo.getUsername();
					loginUserName = loginService.getUserName(loginId);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		String redisKey = "LOGIN||SESSION||" + loginId + "||" + session.getId();
		
		vo = redisService.selectRedisSession(redisKey);
				
		loginId = vo == null ? "" : vo.getLoginId();
		
		logger.info("#### remoteAddr : " + request.getRemoteAddr());
		logger.info("#### loginId : " + loginId);
		logger.info("#### loginUserName : " + loginUserName);
		
		Map map = new HashMap<String, String>();
		
		if (vo != null && loginId != null && !"".equals(loginId) && !"null".equals(loginId)) {
			map.put("loginId", loginId);
			map.put("loginUserName", vo.getLoginUserName());
			
			int sessionTime = 60 * 60 * 5;
			redisService.setTimeOutSecond(redisKey, sessionTime);
		}

		return map;
	}

}

 

@Component
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {

	private final Logger logger = LogManager.getLogger(CustomLoginSuccessHandler.class);

	@Autowired
	private LoginService loginService;

	@Autowired
	private RedisService3 redisService;
	
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {

		String userId = (String)authentication.getPrincipal();

		String loginId = authentication.getName();
		String loginName = loginService.getUserName(loginId);

		HttpSession session = request.getSession();

		String sId = session.getId();
		

		session.setAttribute("loginId", loginId);
		session.setAttribute("loginUserName", loginName);
		
		UserDetailsVO userDetailsVO = new UserDetailsVO();
		int sessionTime = 60 * 60 * 5;
		
		userDetailsVO.setLoginId(loginId);
		userDetailsVO.setLoginUserId1(userId);
		userDetailsVO.setLoginUserName(loginName);
		
		redisService.deleteRedisLikeSession("LOGIN||SESSION||" + loginId + "||*");
		
		// save redis session
		String redisKey = "LOGIN||SESSION||" + loginId + "||" + sId;
		redisService.insertRedisMap(redisKey, new HashMap<String, Object>(userDetailsVO.getMap()));
		redisService.setTimeOutSecond(redisKey, sessionTime);
		
		CookieHandle.setCookie(response, "login_id", loginId);
		CookieHandle.setCookie(response, "session_id", sId);

		response.sendRedirect("/rg/index.jsp");
	}
}

 

public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {

	@Autowired
	private RedisService3 redisService;
	
	@Override
	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		
		HttpSession session = request.getSession(false);
		
		String loginId = CookieHandle.getCookie(request, "login_id");
		String sessionId = CookieHandle.getCookie(request, "session_id");
		
		redisService.processRedisLogout("LOGIN||SESSION||" + loginId + "||" + sessionId);
		
		if (session != null) {
			session.invalidate();
		}
		response.setStatus(HttpServletResponse.SC_OK);
		response.sendRedirect("/");
	}
}

 

Below is the viewing of the redis server.

ubuntu@ip-172-31-27-22:~$ redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
1) "spring:session:sessions:expires:6d51ce20-5aba-4648-91bd-2b4d40f96723"
2) "spring:session:sessions:c876fd17-6f68-46ad-9607-41656c9f6911"
3) "spring:session:sessions:ad469d20-91bf-4060-8a7b-d50aeb8922d9"
4) "spring:session:expirations:1599488220000"
5) "spring:session:sessions:6d51ce20-5aba-4648-91bd-2b4d40f96723"
6) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:rg"
7) "spring:session:sessions:expires:c876fd17-6f68-46ad-9607-41656c9f6911"
8) "spring:session:sessions:expires:ad469d20-91bf-4060-8a7b-d50aeb8922d9"
127.0.0.1:6379> type spring:session:sessions:6d51ce20-5aba-4648-91bd-2b4d40f96723
hash
127.0.0.1:6379> hgetall spring:session:sessions:6d51ce20-5aba-4648-91bd-2b4d40f96723
1) "creationTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01te\xf1\xd9N"
3) "lastAccessedTime"
4) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01te\xf1\xd9N"
5) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
6) "\xac\xed\x00\x05sr\x006org.springframework.security.web.csrf.DefaultCsrfTokenZ\xef\xb7\xc8/\xa2\xfb\xd5\x02\x00\x03L\x00\nheaderNamet\x00\x12Ljava/lang/String;L\x00\rparameterNameq\x00~\x00\x01L\x00\x05tokenq\x00~\x00\x01xpt\x00\x0cX-CSRF-TOKENt\x00\x05_csrft\x00$bb6d297d-c3f0-44be-a61e-00a24c254cdd"
7) "maxInactiveInterval"
8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\xc3P"
127.0.0.1:6379>

If the redis server does not have a password, the auth command is not required.

 

Among the attached files, the UserLoginServiceImpl.java file is a version that does not use spring security.

 

[ Web Page Referenced ]

https://handcoding.tistory.com/137

https://m.blog.naver.com/jooda99/221460700542

https://sabarada.tistory.com/105

https://data-make.tistory.com/545

https://jaepils.github.io/redis/2018/11/20/redis-scan.html

Attachment File
UnitSysAuthVO.java (9,141 byte)
UserDetailsVO.java (15,210 byte)
StringUtil.java (32,879 byte)
RedisService3.java (6,176 byte)
context-redis.xml (2,719 byte)
context-security.xml (2,123 byte)
CookieHandle.java (922 byte)
SHA512.java (610 byte)

Comment

Name               Password 
Content
Check Password.

Please enter your password when registering your comment.