Redis implements Session sharing

Posted by designguy79 on Thu, 30 Dec 2021 19:00:06 +0100

I've been working on such a small module as session sharing these days, and I've also checked a lot of data. It gives me the feeling that it's too messy. I can't find what I want. Almost all the implementation methods are different from my ideas. Here, I summarize how I realize session sharing with redis to facilitate my future query, I also hope to give some help to friends who need it


Let's talk about my development environment: nginx, redis and tomcat. The project is built with moven and the jetty server runs. Therefore, here, we will also talk about how to use maven to package war and deploy it on tomcat.

redis is a key value database. The stored value depends on this key. I won't paste the original and professional introduction here. If you want to know the official introduction, you can search by yourself

 pom. Configuration in XML:

<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>


aplicationContext-redis. Configuration in XML
 

   <!-- redis Client configuration -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${jedisPoolConfig.maxTotal}"/>
        <property name="maxIdle" value="${jedisPoolConfig.maxIdle}"/>
        <property name="maxWaitMillis" value="${jedisPoolConfig.maxWaitMillis}"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
    </bean>
    <bean id="readJedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${jedis.read.host}" />
        <property name="port" value="${jedis.read.port}" />
        <property name="password" value="${jedis.read.password}" />
        <property name="timeout" value="${jedis.read.timeout}" />
        <property name="database" value="${jedis.read.database}" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>
    <bean id="writeJedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${jedis.write.host}" />
        <property name="port" value="${jedis.write.port}" />
        <property name="password" value="${jedis.write.password}" />
        <property name="timeout" value="${jedis.write.timeout}" />
        <property name="database" value="${jedis.write.database}" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>
    <bean id="readRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="readJedisConnectionFactory" />
    </bean>
    <bean id="writeRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="writeJedisConnectionFactory" />
    </bean>


After configuration, start code implementation:

In LoginController:

Step 1: introduce RedisTemplate

@Autowired
@Qualifier("writeRedisTemplate")
private StringRedisTemplate writeTemplate;

Here, you only need to import writeRedisTemplate. When logging in, you are only responsible for writing. Only when refreshing again and passing through the filter, you need to read

The second step is the normal login process. After the login is successful, the request also needs to save the session information

Step 3: set the cookie value and store the key value in redis as the saved userSession information in the cookie. When refreshing the browser, the filter can get the key value from the cookie, and then go to redis to get the corresponding value, that is, userSession

    String domain = request.getServerName();
        String cookieId=MD5Util.MD5Encode("uasLoginer", "UTF-8");
        //Generate a token to be used as the key value of the session in redis storage
        StringredisSessionKey= UUID.randomUUID().toString();
        Cookie uasLoginer = new Cookie(cookieId, redisSessionKey);
        if (domain.startsWith("uas.")) {
        uasLoginer.setDomain(domain.substring(4,domain.length()));
    }else {
        uasLoginer.setDomain(domain);
    }
        uasLoginer.setMaxAge(60000);
        uasLoginer.setPath("/");
        response.addCookie(uasLoginer);


Here, cookie s are set across domains setDomain and setPath

Step 4: store the userSession information in redis

If the value of redis written in RedisTemplate is of String type, the userSession object needs to be converted into Json String

    userSessionString = JSON.toJSONString(userSession);
If you encounter problems when transferring to Jason, import it to import com alibaba. fastjson. JSON; Failed all the time. It is found that there is no dependency on Jason in pom. If you encounter the same problem, you can check it in pom Whether there are JSON dependencies in XML, if not, in pom The dependency of importing JSON into XML is as follows:

    <dependency>
        <groupId>net.sf.json-lib</groupId>
        <artifactId>json-lib</artifactId>
        <version>2.3</version>
        <classifier>jdk15</classifier>
    </dependency>


The code written to redis is as follows:

    writeTemplate.opsForHash().put(UasContants.REDIS_USER_SESSION_KEY+"_"+redisSessionKey,redisSessionKey, userSessionString);
    writeTemplate.expire(UasContants.REDIS_USER_SESSION_KEY+"_"+redisSessionKey, 1800L, TimeUnit.SECONDS);// Set the validity period of the value in redis


After this operation, the user's session information has been saved in redis. You can check whether it is saved in redis.

Step 5: after entering the page, refresh the page, and the request will pass through the filter. In filter Read the value of redis in Java and do some processing

In the filter, redisTemplate cannot be introduced through annotation. It can be introduced through the following methods:

    BeanFactory beans = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
      StringRedisTemplate readTemplate = (StringRedisTemplate) beans.getBean("readRedisTemplate");
      StringRedisTemplate writeTemplate = (StringRedisTemplate) beans.getBean("writeRedisTemplate");
//The filter takes the key value of redis from the cookie and reads the value value with readTemplate

    String cookid=MD5Util.MD5Encode("uasLoginer", "UTF-8");
    Cookie[] cookies = req.getCookies();
    String redisSessionKey = "";
    if(cookies != null){
        for (Cookie cookie : cookies) {
            if(cookie.getName().equals(cookid)){
                redisSessionKey = cookie.getValue() ;
            }
        }
    }
    UserSession userSession = null;
    String userSessionString = (String) readTemplate.boundHashOps(UasContants.REDIS_USER_SESSION_KEY+"_"+redisSessionKey).get(redisSessionKey);
    if(null != userSessionString ){
        @SuppressWarnings("static-access")
        JSONObject obj = new JSONObject().fromObject(userSessionString);//Convert json string to json object
        userSession = (UserSession)JSONObject.toBean(obj,UserSession.class);
        writeTemplate.expire(UasContants.REDIS_USER_SESSION_KEY+"_"+redisSessionKey, 1800L, TimeUnit.SECONDS);
        request.getSession().setAttribute(UasContants.USER_SESSION, userSession);
    }
    if (userSession != null) {
        chain.doFilter(req, res);
        return;
    }else {
        res.sendRedirect(UasContants.LOGIN_URL);
        return;
    }


Here, we also attach the information about web XML for LoginFilter configuration, please refer to the following if necessary:

    <!-- Spring monitor -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>com.sfbest.uas.filter.LoginFilter</filter-class>
        <init-param>
            <param-name>excludePaths</param-name>
            <param-value>/login,/user/login,/user/auth</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


According to the above configuration, you can use redis to realize the function of session sharing. However, when I was developing, I encountered an egg pain problem. In the test environment,

When the project is deployed on two tomcat servers, the redis key value cannot be saved in the cookie. A single server can be saved. After long-term testing,

Finally, it was found that it was the problem of nginx configuration, which was a warning and a deep shadow. Below I post the configuration code of nginx when I am running normally

upstream uassessiontest.d.com {
        server 10.103.16.226:8088;
        server 10.103.16.226:8089;
        }
       server {
                  log_format  sf_uastest  '$remote_addr - $remote_user [$time_local] "$request" '
                                        '$status $body_bytes_sent "$http_referer" '
                                        '"$http_user_agent" $http_cookie';
                listen 80;
                server_name uassessiontest.d.com;
                access_log /var/log/nginx/uassessiontest.log sf_uastest;
 
                location / {
                        rewrite ^/$ /uas/ break;
                        proxy_pass http://uassessiontest.d.com;
                }
        }


The red ones are the missing parts. These parts are used to write cookie values to the browser
--------
Copyright notice: This is the original article of CSDN blogger "robs powder", which follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.
Original link: https://blog.csdn.net/zouxucong/article/details/53286748

Topics: Web Development