springboot + springmvc sharing session

Disclaimer: This article is a blogger original article, please indicate the source. https://blog.csdn.net/u010597819/article/details/91357930

Why sharing session

  1. Enhance the user experience: If you had to change the way a machine can be the main core state when the user disconnects the user information Reply
  2. Provide high availability service: a server goes down to the user can be done almost no awareness, to provide a stable service available

Since the advent of the Internet era, the influx of a large number of Internet users, giving rise to a lot of stand-alone scenario can not be met, after all, concurrency and performance stand-alone has limitations. So they gave birth to distributed applications, the advent of distributed services is bound to solve all of the operations of each machine after a user logs on to the back-end distributed services are visible. If after the first operation request server A top hit, hit the second request server B above, a user operation to be performed on the same server AB are visible, but not to say that the second server B to log in and before repeating the operation has been treated thing again. After all, today's Internet is against the clock

Shared realization of session

Share realize the session, to be persistent session. Such as a common mysql, redis and so on. Then we look at springboot + springmvc shared achieve the session. We realize redis background to getting into the source code
automatically configured springboot for us ready environment, users need only a simple configuration can be used

  1. Dependent on configuration
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
  1. Configuration Properties
# 设置session刷新ON_SAVE(表示在response commit前刷新缓存),IMMEDIATE(表示只要有更新,就刷新缓存)
spring.session.redis.flush-mode=on_save
# 选择使用redis 作为session存储
spring.session.store-type=redis

Yes, that is a simple two configurations, we do share the session. Then we take a look at springboot automatic configuration are doing for us?
He spent the last article of the students should remember SessionHandler handle jetty services. Yes look at the name to know is a container used to process the session. Not seen or forget the students can follow the mass walk-wing doors ha ha https://blog.csdn.net/u010597819/article/details/90745546
seen students can follow a direct role we continue to look at SessionHandler handle

SessionHandler

Jetty container starts callback handler: doStart

  1. handler when the Server service starts vessel will also manage registration and start to destroy a variety of services and Handler's Filter, SessionHandler start doStart
  2. Gets Jetty Server Object
  3. If sessionCache cache is empty, initialization and caching
  4. Get SessionCacheFactory instance from the collection _beans Server instance management, if empty default implementation is used DefaultSessionCache
  5. Get SessionDataStoreFactory instance from the collection _beans Server instance management, if the default is empty achieve NullSessionDataStore
  6. If _sessionIdManager is empty
  7. Server objects get the sessionIdManager assignment, if still empty, create the default implementation DefaultSessionIdManager
  8. The collection _beans DefaultSessionIdManager registered to Server manages and start DefaultSessionIdManager.start
  9. Adding DefaultSessionIdManager to the current set of SessionHandler managed _beans
  10. Get Scheduler achieve from _beans Server manages the collection, if the default ScheduledExecutorScheduler is created empty and start start
  11. If not empty ContextHandler
  12. Get value from the value corresponding to the context org.eclipse.jetty.servlet.SessionCookie _initParams property map as cookie name, default: JSESSIONID
  13. Also acquired from the value corresponding to the value of org.eclipse.jetty.servlet.SessionIdPathParameterName _initParams properties as _sessionIdPathParameterName, _sessionIdPathParameterNamePrefix
  14. If the maximum equals -1 _maxCookieAge same org.eclipse.jetty.servlet.MaxAge attempt to obtain the corresponding value from the attribute value _initParams long as the cookie
  15. The same manner _sessionDomain, _sessionPath, _checkingRemoteSessionIdEncoding
  16. Creating a context SessionContext
  17. Initialize cache initialization _sessionCache
protected void doStart() throws Exception
{
    //check if session management is set up, if not set up HashSessions
    final Server server=getServer();
    ...
    synchronized (server)
    {
        //Get a SessionDataStore and a SessionDataStore, falling back to in-memory sessions only
        if (_sessionCache == null)
        {
            SessionCacheFactory ssFactory = server.getBean(SessionCacheFactory.class);
            setSessionCache(ssFactory != null?ssFactory.getSessionCache(this):new DefaultSessionCache(this));
            SessionDataStore sds = null;
            SessionDataStoreFactory sdsFactory = server.getBean(SessionDataStoreFactory.class);
            if (sdsFactory != null)
                sds = sdsFactory.getSessionDataStore(this);
            else
                sds = new NullSessionDataStore();
            
            _sessionCache.setSessionDataStore(sds);
        }
        if (_sessionIdManager==null)
        {
            _sessionIdManager=server.getSessionIdManager();
            if (_sessionIdManager==null)
            {
                //create a default SessionIdManager and set it as the shared
                //SessionIdManager for the Server, being careful NOT to use
                //the webapp context's classloader, otherwise if the context
                //is stopped, the classloader is leaked.
                ClassLoader serverLoader = server.getClass().getClassLoader();
                try
                {
                    Thread.currentThread().setContextClassLoader(serverLoader);
                    _sessionIdManager=new DefaultSessionIdManager(server);
                    server.setSessionIdManager(_sessionIdManager);
                    server.manage(_sessionIdManager);
                    _sessionIdManager.start();
                }
                finally
                {
                    Thread.currentThread().setContextClassLoader(_loader);
                }
            }
            // server session id is never managed by this manager
            addBean(_sessionIdManager,false);
        }
        _scheduler = server.getBean(Scheduler.class);
        if (_scheduler == null)
        {            
            _scheduler = new ScheduledExecutorScheduler();
            _ownScheduler = true;
            _scheduler.start();
        }
    }
    // Look for a session cookie name
    if (_context!=null)
    {
        String tmp=_context.getInitParameter(__SessionCookieProperty);
        if (tmp!=null)
            _sessionCookie=tmp;
        tmp=_context.getInitParameter(__SessionIdPathParameterNameProperty);
        if (tmp!=null)
            setSessionIdPathParameterName(tmp);
        // set up the max session cookie age if it isn't already
        if (_maxCookieAge==-1)
        {
            tmp=_context.getInitParameter(__MaxAgeProperty);
            if (tmp!=null)
                _maxCookieAge=Integer.parseInt(tmp.trim());
        }
        // set up the session domain if it isn't already
        if (_sessionDomain==null)
            _sessionDomain=_context.getInitParameter(__SessionDomainProperty);
        // set up the sessionPath if it isn't already
        if (_sessionPath==null)
            _sessionPath=_context.getInitParameter(__SessionPathProperty);
        tmp=_context.getInitParameter(__CheckRemoteSessionEncoding);
        if (tmp!=null)
            _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
    }
    _sessionContext = new SessionContext(_sessionIdManager.getWorkerName(), _context);             
   _sessionCache.initialize(_sessionContext);
    super.doStart();
}

Request Request chain handle callbacks: doScope

  1. Get SessionHandler Request request in the session and cookie
  2. If the child node _nextScope and _outerScope node, there is a recursive call
  3. doHandle call the next node handle
  4. Submit session: complete
  5. If the request is asynchronous and is requesting the establishment of Session Request asynchronous monitor SessionAsyncListener, waiting for the operation callback session
  6. If the request is not and is not a Request asynchronous request, submitted synchronization session: complete, cache to _sessionCache, if there is configured timeout timeout monitoring _sessionInactivityTimer create a scheduled task: SessionInactivityTimer

springboot automatic configuration SessionAutoConfiguration sharing session

  1. Start SessionAutoConfiguration automatic configuration after DataSourceAutoConfiguration, redis, such as automatic configuration
  2. Import SessionConfigurationImportSelector
  3. Introducing all storage type configuration StoreType
@Configuration
@ConditionalOnMissingBean(SessionRepository.class)
@ConditionalOnClass(Session.class)
@ConditionalOnWebApplication
@EnableConfigurationProperties(SessionProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HazelcastAutoConfiguration.class,
		JdbcTemplateAutoConfiguration.class, MongoAutoConfiguration.class,
		RedisAutoConfiguration.class })
@Import({ SessionConfigurationImportSelector.class, SessionRepositoryValidator.class })
public class SessionAutoConfiguration {
	/**
	 * {@link ImportSelector} to add {@link StoreType} configuration classes.
	 */
	static class SessionConfigurationImportSelector implements ImportSelector {

		@Override
		public String[] selectImports(AnnotationMetadata importingClassMetadata) {
			StoreType[] types = StoreType.values();
			String[] imports = new String[types.length];
			for (int i = 0; i < types.length; i++) {
				imports[i] = SessionStoreMappings.getConfigurationClass(types[i]);
			}
			return imports;
		}
	}
	/**
	 * Bean used to validate that a {@link SessionRepository} exists and provide a
	 * meaningful message if that's not the case.
	 */
	static class SessionRepositoryValidator {
	...
	}

}

RedisSessionConfiguration配置

  1. SpringBootRedisHttpSessionConfiguration configuration, and the configuration of its parent
  2. SessionProperties injection configuration (prefix spring.session), the configuration session, redis namespace namespace configuration, flushmode configuration
  3. SpringHttpSessionConfiguration配置
  4. Cookie serialization implementation, the default is DefaultCookieSerializer
  5. SessionRepositoryFilter, session repository filter configuration, all interception SessionRepository repository, Servlet.Filter This filter implements the interface, the session data is requested container operable to perform persistence shared
  6. RedisHttpSessionConfiguration配置
  7. EnableScheduling start classes scheduled task support, if cleanupCron is not configured by default to clean up the expression: "0 * * * * *", that is, 0 seconds every minute of time will clear up once
  8. Create the configuration repository RedisOperationsSessionRepository
  9. Create a configuration RedisMessageListenerContainer, listening session to create, delete, overtime
  10. Create a connection factory configuration redis
  11. redis task executor injection springSessionRedisTaskExecutor
  12. 注入springSessionRedisSubscriptionExecutor
  13. Introducing CI EnableRedisHttpSession annotation configuration and current configuration is disposed cover: maxInactiveIntervalInSeconds, redisNamespace, redisFlushMode, cleanupCron
  14. Configuration regular cleaning task, cleaning timeout session

Session created

Cas services use the company's services, the stack below, you can see the acquisition session, getSession by cas interceptors at login, if the session does not exist, create session

java.lang.Thread.State: RUNNABLE
	at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:391)
	at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:217)
	at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:279)
	at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:279)
	at org.pac4j.core.context.session.J2ESessionStore.getHttpSession(J2ESessionStore.java:24)
	at org.pac4j.core.context.session.J2ESessionStore.get(J2ESessionStore.java:34)
	at org.pac4j.core.context.session.J2ESessionStore.get(J2ESessionStore.java:19)
	at org.pac4j.core.context.WebContext.getSessionAttribute(WebContext.java:94)
	at com.....authentication.filters.CasSecurityFilter.doFilter(CasSecurityFilter.java:62)

SessionRepositoryFilter.SessionRepositoryRequestWrapper.getSession

  1. The current scene baseRequest packaged as SessionRepositoryRequestWrapper, getSession (boolean create) is rewritten
  2. If the current currentSession not directly return empty, otherwise continue
  3. Get sessionId, SessionRepositoryFilter.getRequestedSessionId, acquired in accordance with the strategy session httpSessionStrategy
  4. Obtaining session from the session repository SessionRepositoryFilter.this.sessionRepository (RedisOperationsSessionRepository) according to sessionId, read redis
  5. If the session is to identify and create space for false return null
  6. If the session identifier and creates empty session is created and is true for the packages returned HttpSessionWrapper
  7. session repository created session: RedisOperationsSessionRepository.createSession

SessionRepositoryFilter(springSessionRepositoryFilter)

doFilter parent OncePerRequestFilter the callback class method doFilterInternal

  1. Use SessionRepositoryRequestWrapper packaging request
  2. SessionRepositoryResponseWrapper packaging after use request and response package
  3. _HttpSessionStrategy_ policy (e.g.: CookieHttpSessionStrategy) the request and response package after packaging HttpServletRequest, HttpServletResponse
  4. Call filtering chain
  5. Submit session: SessionRepositoryRequestWrapper.commitSession, using _HttpSessionStrategy policies written response to sessionId and persistence session_

to sum up

sharing session : spring SessionRepositoryFilter through the filter will encapsulate the Request Type Request Create request session, session selection policy from the repository _RedisOperationsSessionRepository _HttpSessionStrategy session for persistence of the session is shared SessionRepositoryRequestWrapper type. Of course, there is the case of our sessionHandler cas If no other configurations default to session no action that is NullSessionDataStore
Jetty properties doScope, doHandle

 * <p>For example if Scoped handlers A, B &amp; C were chained together, then
 * the calling order would be:</p>
 * <pre>
 * A.handle(...)
 *   A.doScope(...)
 *     B.doScope(...)
 *       C.doScope(...)
 *         A.doHandle(...)
 *           B.doHandle(...)
 *              C.doHandle(...)
 * </pre>
 *
 * <p>If non scoped handler X was in the chained A, B, X &amp; C, then
 * the calling order would be:</p>
 * <pre>
 * A.handle(...)
 *   A.doScope(...)
 *     B.doScope(...)
 *       C.doScope(...)
 *         A.doHandle(...)
 *           B.doHandle(...)
 *             X.handle(...)
 *               C.handle(...)
 *                 C.doHandle(...)
 * </pre>

Guess you like

Origin blog.csdn.net/u010597819/article/details/91357930