springboot integrates session to realize session sharing

foreword

Session and cookie are two ways to save user state, session is on the server side, and cookie is on the client side.
This article will record the demo example of springboot integrated session.

session (session)

  • Sticky sessions
    If a server goes down, the session information is gone.
  • Replication session
    Each machine replicates the session, if the amount is too large, it is unrealistic
  • Concentrate sessions
    Use mongo, redis, etc. to maintain sessions uniformly

pom dependencies

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-core</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-test</artifactId>
			<scope>test</scope>
		</dependency>

startup class

@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SessionDemoApplication.class, args);
	}
	@RequestMapping("/hello")
	public String printSession(HttpSession session, String name) {
		String storedName = (String) session.getAttribute("name");
		if (storedName == null) {
			session.setAttribute("name", name);
			storedName = name;
		}
		return "hello " + storedName;
	}

}

@EnableRedisHttpSession Use this annotation to enable automatic configuration of httpsession redis

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {
}

Then look at RedisHttpSessionConfiguration

@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
		implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
		SchedulingConfigurer {

	static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";

	private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;

	private String redisNamespace = RedisOperationsSessionRepository.DEFAULT_NAMESPACE;

	private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;

	private String cleanupCron = DEFAULT_CLEANUP_CRON;

	private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();

	private RedisConnectionFactory redisConnectionFactory;

	private RedisSerializer<Object> defaultRedisSerializer;

	private ApplicationEventPublisher applicationEventPublisher;

	private Executor redisTaskExecutor;

	private Executor redisSubscriptionExecutor;

	private ClassLoader classLoader;

	private StringValueResolver embeddedValueResolver;
	...

You can see that it implements some configurations for redis, and RedisHttpSessionConfiguration inherits SpringHttpSessionConfiguration, so take a look at the source code of this class:

@Configuration
public class SpringHttpSessionConfiguration implements ApplicationContextAware {

	private final Log logger = LogFactory.getLog(getClass());

	private CookieHttpSessionIdResolver defaultHttpSessionIdResolver = new CookieHttpSessionIdResolver();

	private boolean usesSpringSessionRememberMeServices;

	private ServletContext servletContext;

	private CookieSerializer cookieSerializer;

	private HttpSessionIdResolver httpSessionIdResolver = this.defaultHttpSessionIdResolver;

	private List<HttpSessionListener> httpSessionListeners = new ArrayList<>();

	@PostConstruct
	public void init() {
		CookieSerializer cookieSerializer = (this.cookieSerializer != null)
				? this.cookieSerializer
				: createDefaultCookieSerializer();
		this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
	}

	@Bean
	public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
		return new SessionEventHttpSessionListenerAdapter(this.httpSessionListeners);
	}

	@Bean
	public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
			SessionRepository<S> sessionRepository) {
		SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
				sessionRepository);
		sessionRepositoryFilter.setServletContext(this.servletContext);
		sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
		return sessionRepositoryFilter;
	}
......

SpringHttpSessionConfiguration is also a configuration class, and it registers some beans. Let's take a look at this filter Bean SessionRepositoryFilter :

@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
...
	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
				request, response, this.servletContext);
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
				wrappedRequest, response);

		try {
			filterChain.doFilter(wrappedRequest, wrappedResponse);
		}
		finally {
			wrappedRequest.commitSession();
		}
	}

...
}


abstract class OncePerRequestFilter implements Filter {

}

You can see that SessionRepositoryFilter inherits OncePerRequestFilter and implements the filter filter.
Let's take a look at the doFilterInternal method, which encapsulates the request once.

		private SessionRepositoryRequestWrapper(HttpServletRequest request,
				HttpServletResponse response, ServletContext servletContext) {
			super(request);
			this.response = response;
			this.servletContext = servletContext;
		}

		@Override
		public HttpSessionWrapper getSession(boolean create) {
			HttpSessionWrapper currentSession = getCurrentSession();
			if (currentSession != null) {
				return currentSession;
			}
			S requestedSession = getRequestedSession();
			if (requestedSession != null) {
				if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
					requestedSession.setLastAccessedTime(Instant.now());
					this.requestedSessionIdValid = true;
					currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
					currentSession.setNew(false);
					setCurrentSession(currentSession);
					return currentSession;
				}
			}
			else {
				// This is an invalid session id. No need to ask again if
				// request.getSession is invoked for the duration of this request
				if (SESSION_LOGGER.isDebugEnabled()) {
					SESSION_LOGGER.debug(
							"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
				}
				setAttribute(INVALID_SESSION_ID_ATTR, "true");
			}
			if (!create) {
				return null;
			}
			if (SESSION_LOGGER.isDebugEnabled()) {
				SESSION_LOGGER.debug(
						"A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
								+ SESSION_LOGGER_NAME,
						new RuntimeException(
								"For debugging purposes only (not an error)"));
			}
			S session = SessionRepositoryFilter.this.sessionRepository.createSession();
			session.setLastAccessedTime(Instant.now());
			currentSession = new HttpSessionWrapper(session, getServletContext());
			setCurrentSession(currentSession);
			return currentSession;
		}

We see that the getSeesion method is also provided in SessionRepositoryRequestWrapper, and the logic of getting the session is here. Let me look at the method of SessionRepositoryFilter.this.sessionRepository.createSession():
click to open its implementation

	@Override
	public RedisSession createSession() {
		RedisSession redisSession = new RedisSession();
		if (this.defaultMaxInactiveInterval != null) {
			redisSession.setMaxInactiveInterval(
					Duration.ofSeconds(this.defaultMaxInactiveInterval));
		}
		return redisSession;
	}

It was found that it uses redis. At this point, the process of using redis as a session is basically completed.

Then we visit
http://localhost:8080/hello?name=spring
insert image description here
and we see that the session is returned in the response header in the first request, and the result is returned

hello spring

Change the name and try:
insert image description here
I found that there is no set-cookies in the Response header, and there is cookie information in the request header. And the return result has not changed:

hello spring

redis

Check out redis, I use docker to run redis

> docker exec -it redis bash
> redis-cli
> keys *
 1) "spring:session:expirations:1560958020000"
 2) "spring:session:expirations:1560958140000"
 3) "spring:session:sessions:51ffe6c7-9007-4ab9-9e90-0767189e5bf7"
 4) "spring:session:sessions:056a46cf-2fbc-4f33-ace3-46d5122177eb"
 5) "spring:session:sessions:expires:51ffe6c7-9007-4ab9-9e90-0767189e5bf7"
 6) "spring:session:sessions:0fe747b1-691a-4ddf-93f1-8e0a5e538c71"
 7) "spring:session:sessions:expires:056a46cf-2fbc-4f33-ace3-46d5122177eb"
 8) "spring:session:sessions:expires:0fe747b1-691a-4ddf-93f1-8e0a5e538c71"

> hkeys "spring:session:sessions:0fe747b1-691a-4ddf-93f1-8e0a5e538c71"
1) "sessionAttr:name"
2) "creationTime"
3) "lastAccessedTime"
4) "maxInactiveInterval"

Use the redis tool rdb to view:
insert image description here
This is the end of this article.

Guess you like

Origin blog.csdn.net/wjavadog/article/details/92851531