Every Internet company today has to use distributed sessions, even small startups have to use sessions all the time. Here is a summary of the three common ways to use session, recording for the convenience of my later review, a good memory is better than bad writing… To understand sessions, you need to know about cookies, which we won’t discuss here.

There are three common Session solutions:

1.Tomcat + Redis

RedisSessionManager is configured in the Tomcat container context. XML file. Portal: [blog.csdn.net/qq_35830949…]. The downside of this is that it is too heavily coupled to the Tomcat container. If you want to switch to Jetty later on, this is not too painful.

2.Spring Session + Redis

This method is currently used more, see SpringSession Github code maintenance is also very active. Compare this with the session management mode configured in the Tomcat container above:

The principle of SpringSession is simply understood: The Session is separated from the Web container and stored in a separate storage server. Session storage services are supported in various forms, such as Redis, Database, and MogonDB. Session management is delegated to SpringSession. When the user requests to enter the Web container and obtains the session according to the request, SpringSession is responsible for obtaining the session from the memory such as Redis. If it exists, the session will be returned; if it does not exist, it will be created and persisted to the Redis storage. The session is written into Redis each time the user requests login, which is automatically deleted when the user logs out. This process is handled by the SpringSession agent.

The official website of SpringSession is maintained actively, so there is no need to worry about insecurity. If there are other problems, the official will also actively fix them. The following figure is proof:

start.spring.io/

  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.session</groupId>
          <artifactId>spring-session-data-redis</artifactId>
      </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-tomcat</artifactId>
         <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
     </dependency>

     <dependency>
         <groupId>redis.clients</groupId>
         <artifactId>jedis</artifactId>
         <version>2.9.0</version>
     </dependency>
 </dependencies>
Copy the code

The SpringBoot application. Properties project is configured with SpringSession connecting to Redis:

server.port=9099 # Session store type. spring.session.store-type=redis # Session timeout. If a duration suffix is not Specified, seconds is 2. Server. The servlet. The session. The timeout = 7200 # Sessions flush mode. # session update strategy, have ON_SAVE, IMMEDIATE, # # is in the call SessionRepository# save (org. Springframework. Session. The session), # in response to refresh the cache before the commit, # which is as long as there is any update will refresh buffer spring. The session. Redis. The flush mode = on - save # Namespace for keys, informs the to store sessions. spring.session.redis.namespace=spring:session # Redis server host. spring.redis.host=localhost # Login password of the redis server. spring.redis.password= # Redis server port. spring.redis.port=6379 spring.redis.jedis.pool.max-active=100 spring.redis.jedis.pool.max-wait=10 spring.redis.jedis.pool.max-idle=10 spring.redis.jedis.pool.min-idle=10Copy the code

Enable Redis Http SpringSession

/** * Open Redis Http Session */
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfiguration {}Copy the code

Write a Controller test

@RestController
public class SessionController {

    @RequestMapping("/session")
    public Object springSession(@RequestParam("username") String username,
                                HttpServletRequest request, HttpSession session) {
        Cookie[] cookies = request.getCookies();
        if(cookies ! =null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().contains("JSESSION")) {
                    System.out.println(cookie.getName() + "=" + cookie.getValue());
                }
            }
        }

        Object value = session.getAttribute("username");
        if (value == null) {
            System.out.println("User does not exist");
            session.setAttribute("username"."{username: '" + username + "', age: 28}");
        } else {
            System.out.println("User exists");
        }

        return "username="+ value; }}Copy the code

Finally, the Redis tool looks at the data:

3.JWT(JSON Web token) + Redis mode

IO /] [jwt.io/] I’m not going to explain it here. Just using her mind. The simple implementation version of token Redis currently in use. The realization idea of this way:

(1) A token is generated when the user logs in to the system

Token= uuID.toString ().replact(“-“,””) then call redisClient to write redis and json returns body{Token :” XXXXX222 “,” other fields “:” Other fields “}

redisClient.set("user:id:" + token, userId, timeInterval);
Copy the code
(2) User login to access other interface annotation + interceptor
/** * The interface to which this annotation is added does not perform token authentication and directly accesses the backend service */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginIgnore {
    boolean value(a) default true;
}
Copy the code

Here is the interceptor implementation:

@Slf4j
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            LoginIgnore loginIgnore = method.getMethodAnnotation(LoginIgnore.class);
            if (Objects.nonNull(loginIgnore)) {
                return true;
            }
            String token = "";
            // Request the token through the httpRequest header
            Enumeration<String> enumeration = request.getHeaderNames();
            while (enumeration.hasMoreElements()) {
                String name = enumeration.nextElement();
                if ("token".equals(name.toLowerCase())) { token = request.getHeader(name); }}// Verify whether the token has been logged in and expired. If expired, deny access to the interface
            if ("".equals(token) || userService.isTokenExpire(token)) {
                log.error("Token :{}, methodName:{}", token, method.getMethod().getName());
                this.sendResponse(response,  "Login invalid");
                return false;
            }
            Integer userId = userService.getUserIdByToken(token);
            if (Objects.isNull(userId)) {
                this.sendResponse(response, "Login invalid");
                return false;
            }
            request.setAttribute("userId", userId);
            request.setAttribute("token", token);
        }
        return true;
    }

    /** * processing error */
    private void sendResponse(HttpServletResponse response, String error) throws IOException {
        response.setHeader("Content-type", MediaType.APPLICATION_JSON.toString());
        response.setCharacterEncoding("UTF-8"); Result result = Result.wrapError(error); response.getWriter().write(JSON.toJSONString(result)); }}public Boolean isTokenExpire(String token) {
        Assert.hasText(token, "Token cannot be empty");
        return! redisClient.exists("user:id:" + token);
    }

 public Integer getUserIdByToken(String token) {
     Assert.hasText(token, "Token cannot be empty");
     return redisClient.get("user:id:" + token);
 }
Copy the code
(3) The user logs out and knows the token in Redis
redisClient.del("user:id:" + token,userId);
Copy the code

Conclusion:

Distributed Session there are many ways to implement distributed Session. Tomcat+Redis is the most common technology in the early stage, but this way is heavily coupled with containers and difficult to maintain. Currently, it is common to use SpringSession to centrally manage sessions. Using sessions in this way is not customizable and flexible enough. If you are a big company with a lot of money, you will develop Session management.