Spring Session
λ³Έ κΈμ λ λμ€ κ΄λ ¨ κ²°ν¨μ κ²½νν κ²μ ν λλ‘ μ€νλ§ μΈμ λ λμ€μ λν λμμ μ 리νκΈ° μν΄μ μμ±ν κ² μ λλ€.
λ§μ μλ° κ°λ°μκ° μ€νλ§ λΆνΈ νλ‘μ νΈλ₯Ό μ¬μ©νλ μ΄μ λ λ³λ€λ₯Έ μ½λ ꡬν μμ΄λ μ¬λ¬ κ°λ°μλ€μ μν΄ μμ±λμ΄μ§ λ‘μ§μ μλμΌλ‘ ꡬμ±νλ©΄μ μ½κ³ λΉ λ₯΄κ² μνλ κΈ°λ₯κ³Ό λμμ μ ν리μΌμ΄μ μ μ μ©μν€κΈ° μν λͺ©μ μ΄ ν¬λ€κ³ μκ°ν©λλ€. νμ§λ§, μλ° λΏλ§ μλλΌ λ€λ₯Έ μΈμ΄μ νλ μμν¬μμλ ꡬνλ μ½λλ€μ μ λΆ νμΈνκ³ μ¬μ©νμ§λ μμ κ²½μ°κ° λ§μν λ°μ. μ€νλ§ νλ μμν¬μ ꡬν λ²μκ° μλΉν λ§λ€λ³΄λ μ€νλ§ νλ μμν¬μ κ°λ νΉμ λμμ λν΄μ λμΆ© μ΄ν΄νκ³ λμ΄κ°κ±°λ μλ μ μμ μ¬μ©νλ νΈμ λλ€.
μ€νλ§ μΈμ λ λμ€μ μν κ²°ν¨μ λ§λ€μ΄λΈ μ΄μ λ RequestContextHolderλ₯Ό ν΅ν΄μ μ€λ λ λ‘컬μ μ μ₯λ μμ² μ 보λ₯Ό κ°μ Έμ¬ μ μκ³ RequestContextHolderλ‘ λΆν° κ°μ Έμ¨ RequestAttributesμ μΈμ μμ΄λλ₯Ό κ°μ Έμ¬ μ μλ ν¨μκ° μκΈ°μ μ¬μ©νλ κ²μΌλ‘λΆν° μμλ©λλ€. κ°μΈμ μΈ κ²½νμΌλ‘ λ³Όλλ μ€νλ§ μΈμ κ³Ό λ λμ€λ₯Ό ν¨κ» μ¬μ©νλ κ²μ λ¨μνκ² μ€νλ§ μΈμ λ λμ€μ λν μμ‘΄μ±λ§ μΆκ°νκ³ λ λμ€μ μ°κ²°ν μ μλ μ 보 κ·Έλ¦¬κ³ μ€νλ§ μΈμ μ νμ±ννλ μ΄λ Έν μ΄μ μ μΆκ°νλ κ² λΏμ΄λ―λ‘ λ΄μ¬λ μ½λκ° μ΄λ»κ² λμνλμ§ μ λλ‘ νμΈν νμμ±μ μμμ΅λλ€.
TCPμ HTTPμ μΈμ μ λ€λ₯΄λ€.
Spring Session provides transparent integration with HttpSession. This means that developers can switch the HttpSession implementation out with an implementation that is backed by Spring Session.
TCPμμμ μΈμ μ μ°κ²°μ μλ―Ένμ§λ§ HTTPμμμ μΈμ μ μ°κ²°μ λν μνλ₯Ό μλ―Έν©λλ€. μ€νλ§ μΈμ μ TCP λ λ²¨μ΄ μλ HTTP μΈμ μ λν ν΅ν©μ μ§μν©λλ€. κ·Έλ¦¬κ³ κΈ°λ³Έμ μΌλ‘λ λ©λͺ¨λ¦¬μ μΈμ μ μ μ₯νκ² λλ κ²μ JDBC κΈ°λ°μΌλ‘ κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μ μ μ₯νλ€κ±°λ λ λμ€λ₯Ό μ¬μ©ν΄μ μΈμ μ μ₯μλ‘ νμ©ν μ μλλ‘ μ 곡νλ κ²λ ν¬ν¨νκ³ μμ΅λλ€.
WASλ μΈμ κ΄λ¦¬λ₯Ό μ§μνλ€.
ν°μΊ£μ΄λ μΈλν μ°μ κ°μ WASμμλ μ체μ μΌλ‘ λ©λͺ¨λ¦¬ κΈ°λ°μ μΈμ μ μ§μνλλ‘ κ΅¬νλμ΄μμ΅λλ€. μ€νλ§ μΈμ μμλ μλΈλ¦Ώ 컨ν μ΄λ(WAS)κ° μ체μ μΈ μΈμ μ μμ±νμ§ μλλ‘ AbstractHttpSessionApplicationInitializerλ₯Ό ν΅ν΄ springSessionRepositoryFilter μ΄λΌλ μ΄λ¦μ νΉμν νν°λ₯Ό λ±λ‘νμ¬ λͺ¨λ μμ²μ λν΄μ μ²λ¦¬λλλ‘ μꡬν©λλ€.
Fortunately, both HttpSession and HttpServletRequest (the API for obtaining an HttpSession) are both interfaces. This means that we can provide our own implementations for each of these APIs.
This highlights why it is important that Spring Sessionβs SessionRepositoryFilter be placed before anything that interacts with the HttpSession.
μ€νλ§ μΈμ μ μ체 ꡬν μΈμ μΌλ‘ μ ννλ€.
SessionRepositoryFilterκ° μννλ μ€μν μν μ μλ° μλΈλ¦Ώ μ€νμ HTTP μΈμ μ μ체μ μΈ μΈμ ν΄λμ€λ‘ μ ννλ κ² μ λλ€. κ·Έλ¦¬κ³ λ΄λΆμ μΌλ‘ HTTP μΈμ κ³Ό μ€νλ§ μΈμ μ μ°κ²°νκΈ° μν΄μ μΏ ν€ κΈ°λ°μ HttpSessionIdResolverλ₯Ό μ¬μ©νλλ‘ λμ΄μμ£ . κ²°κ΅ SessionRepository ꡬν체μ λ°λΌ JDBC κΈ°λ°μΌλ‘ λ°μ΄ν°λ² μ΄μ€ μΈμ μ 보λ₯Ό μ μ₯νλμ§ λ λμ€μ μ μ₯νλμ§ κ΅¬λΆλμ΄μ§λ κ²μ λλ€.
Switches the HttpSession implementation to be backed by a Session. The SessionRepositoryFilter wraps the HttpServletRequest and overrides the methods to get an HttpSession to be backed by a Session returned by the SessionRepository.
@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);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}
SessionRepositoryFilterμ μ ꡬνμ²λΌ κ°μ₯ λ¨Όμ μ²λ¦¬λ¨μΌλ‘μ¨ μμ² μ€λ λ λ΄λΆμμ λ³κ²½λμ΄μ§ μΈμ μ λν΄μλ μλ΅μ΄ μλ£λ μ΄νμ μ΅μ’ μ μΌλ‘ λ°μλλ€λ κ²μ μ μ μμ΅λλ€. κ·Έλμ κ²½ννλ λ λμ€ κ²°ν¨μμλ μλΉμ€ νΉμ νΌμμ€ν΄μ€ λ μ΄μ΄μμ μΈμ μ΄ μμ±λλλΌλ μΈμ μ 보λ₯Ό λ λμ€μ μ μ₯νκ² λ κ²μ λλ€.
λ λμ€μ μ μ₯λλ μμΈν λ΄μ©μ μ€νλ§ μΈμ 곡μ λ¬Έμμ Storage Detailsμμ νμΈν μ μμ΅λλ€.
μ€νλ§ μΈμ λ λμ€λ μ€μΌμ€λ§μ ν΅ν΄ λ§λ£λ ν€λ₯Ό μμ νλ€.
SessionCleanupConfigurationμμ RedisSessionExpirationPolicyμ cleanExpiredSessions ν¨μλ₯Ό μ€μΌμ€λ¬μ λ±λ‘νμ¬ μ€νλ§μμ μ체μ μΌλ‘ μ 곡νλ μ€μΌμ€λ§μ μν΄ λ§λ£λ ν€κ° μμ λ©λλ€. λ€λ§, λ λμ€μ λ§λ£ μ΄λ²€νΈμ νμ΄λ° λ¬Έμ λ‘ μΈν΄μ TTLμ΄ λ§λ£λ μ΄νμ λ λμ€κ° μμμ μμ νλλ‘ μ€νλ§ μΈμ λ λμ€μμλ λͺ μμ μΌλ‘ ν€λ₯Ό μμ νμ§ μκ³ λ¨μν μ‘°ν(μ‘μΈμ€)ν©λλ€.
We do not explicitly delete the keys, since, in some instances, there may be a race condition that incorrectly identifies a key as expired when it is not. Short of using distributed locks (which would kill our performance), there is no way to ensure the consistency of the expiration mapping. By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.