序文
セッションと Cookie はユーザー状態を保存する 2 つの方法であり、セッションはサーバー側にあり、Cookie はクライアント側にあります。
この記事では、springboot 統合セッションのデモ例を記録します。
セッション(セッション)
- スティッキーセッション
サーバーがダウンすると、セッション情報が失われます。 - レプリケーションセッション
各マシンがセッションをレプリケートしますが、量が大きすぎると現実的ではありません - セッションを集中させる
mongo、redisなどを利用してセッションを均一に維持する
pom 依存関係
<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>
スタートアップクラス
@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 このアノテーションを使用して、httpsession redis の自動構成を有効にします。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {
}
次に、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;
...
Redis のいくつかの構成が実装されており、RedisHttpSessionConfiguration が SpringHttpSessionConfiguration を継承していることがわかります。そのため、このクラスのソース コードを見てください。
@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 も構成クラスであり、いくつかの Bean を登録します。このフィルター 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 {
}
SessionRepositoryFilter が OncePerRequestFilter を継承し、フィルター フィルターを実装していることがわかります。
リクエストを一度カプセル化する doFilterInternal メソッドを見てみましょう。
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;
}
getSeesion メソッドも SessionRepositoryRequestWrapper で提供されており、セッションを取得するロジックはここにあります。SessionRepositoryFilter.this.sessionRepository.createSession() のメソッドを見てみましょう: その
実装をクリックします。
@Override
public RedisSession createSession() {
RedisSession redisSession = new RedisSession();
if (this.defaultMaxInactiveInterval != null) {
redisSession.setMaxInactiveInterval(
Duration.ofSeconds(this.defaultMaxInactiveInterval));
}
return redisSession;
}
redisを使用していることが分かりました。この時点で、redis をセッションとして使用するプロセスは基本的に完了します。
次に、http://localhost:8080/hello?name=springにアクセスする
と、最初のリクエストの応答ヘッダーでセッションが返され、結果が返されることがわかります。
hello spring
名前を変更して試してみると、
Response ヘッダーには set-cookies がなく、リクエスト ヘッダーには Cookie 情報があることがわかりました。そして返される結果は変わりません:
hello spring
レディス
Redis を確認してください。Redis の実行には docker を使用しています
> 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"
Redis ツール rdb を使用して表示します。
この記事はこれで終わりです。