Spring 시리즈. @ EnableRedisHttpSession 원리 간략 분석

11333 단어
클 러 스 터 시스템 에 서 는 세 션 을 공유 해 야 하 는 경우 가 많다.그렇지 않 으 면 이러한 문제 가 발생 할 수 있 습 니 다. 사용자 가 시스템 A 에 로그 인 한 후에 후속 적 인 작업 이 시스템 B 에 균형 을 맞 추 면 시스템 B 는 본 컴퓨터 에 이 사용자 의 Session 이 없 는 것 을 발견 하면 사용자 가 다시 로그 인 하도록 강제 할 것 입 니 다.이때 사용 자 는 자신 이 분명히 로그 인 했 는데 왜 스스로 다시 로그 인 하려 고 하 는 지 의 심 스 러 울 것 이다.
세 션 이란 무엇 인가
여기 서 세 션 의 개념 을 보급 합 니 다. 세 션 은 서버 측의 key - value 데이터 구조 로 사용자 와 쿠키 에 의 해 자주 결합 되 어 사용자 의 로그 인 메 시 지 를 유지 합 니 다.클 라 이언 트 가 서버 를 처음 방문 할 때 서버 는 sessionid 에 응답 하고 로 컬 쿠키 에 저장 합 니 다. 다음 방문 은 쿠키 의 sessionid 를 요청 헤더 에 넣 고 서버 에 접근 합 니 다. 이 sessionid 를 통 해 해당 하 는 데 이 터 를 찾 지 못 하면 서버 는 새로운 sessionid 를 만 들 고 클 라 이언 트 에 응답 합 니 다.
분산 세 션 솔 루 션
  • 쿠키 를 사용 하여 완성 합 니 다 (이러한 안전 하지 않 은 조작 이 믿 을 수 없 음 이 분명 합 니 다)
  • Nginx 의 ip 바 인 딩 정책 을 사용 합 니 다. 같은 ip 은 지정 한 같은 기기 에서 만 접근 할 수 있 습 니 다 (부하 균형 은 지원 되 지 않 습 니 다)
  • 데이터 베 이 스 를 이용 하여 세 션 동기 화 (효율 이 높 지 않 음)
  • tomcat 에 내 장 된 session 동기 화 사용 (동기 화 지연 가능)
  • session 대신 token 사용
  • 저 희 는 spring - session 과 통 합 된 솔 루 션 을 사용 하여 Redis 에 저장 합 니 다
  • .
    마지막 방안 은 본문 이 소개 하고 자 하 는 중점 이다.
    Spring Session 사용법
    의존 도 를 높이다
    
        org.springframework.session
        spring-session-data-redis
    
    
        org.springframework.session
        spring-session
    

    주석 추가 @ EnableRedisHttpSession
    @Configuration
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
    public class RedisSessionConfig {
    }

    max InactiveIntervalInSeconds: Session 실효 시간 을 설정 하고 Redis Session 을 사용 하면 원래 Spring Boot 의 server. session. timeout 속성 이 유효 하지 않 습 니 다.
    위의 설정 을 통 해 Session 호출 은 자동 으로 Redis 로 접근 합 니 다.또 세 션 공유 의 목적 을 달성 하려 면 다른 시스템 에서 같은 설정 을 하면 된다.
    4. Spring Session Redis 의 원리 에 대한 간략 분석
    위의 설정 을 보면 Redis Session 을 여 는 '비밀' 이 @ EnableRedisHttpSession 이라는 주석 에 있 음 을 알 수 있 습 니 다.@ EnableRedisHttpSession 의 원본 코드 열기:
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(RedisHttpSessionConfiguration.class)
    @Configuration
    public @interface EnableRedisHttpSession {
        //Session      ,    ,  30  
        int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
        //  key namespace,    spring:session,           redis,          namespace,        Session        
        String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
        //    Redis Session   ,   ON_SAVE  ,   Response      Session   Redis
        //          IMMEDIATE  ,       Session         Redis
        RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
        //    Session            。
        String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
    }

    이 주해 의 주요 역할 은 Session Repository Filter 를 등록 하 는 것 입 니 다. 이 Filter 는 모든 요청 을 차단 하고 Session 을 조작 합 니 다. 구체 적 인 조작 세부 사항 은 뒤에서 설명 합 니 다. 이 주해 의 역할 은 Session Repository Filter 를 등록 하면 됩 니 다.Session Repository Filter 를 주입 하 는 코드 는 RedisHttpSession Configuration 클래스 에 있 습 니 다.
    @Configuration
    @EnableScheduling
    public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
            implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
            SchedulingConfigurer {
                ...
            }

    RedisHttpSession Configuration 은 SpringHttpSession Configuration 을 계승 하고 SpringHttpSession Configuration 에는 Session Repository Filter 가 등록 되 어 있 습 니 다.다음 코드 참조.
    @Configuration
    public class SpringHttpSessionConfiguration implements ApplicationContextAware {
        ...
         @Bean
        public  SessionRepositoryFilter extends Session> springSessionRepositoryFilter(
                SessionRepository sessionRepository) {
            SessionRepositoryFilter sessionRepositoryFilter = new SessionRepositoryFilter<>(
                    sessionRepository);
            sessionRepositoryFilter.setServletContext(this.servletContext);
            sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
            return sessionRepositoryFilter;
        }
         ...
    }

    Session Repository Filter 를 등록 할 때 Session Repository 인자 가 필요 한 것 을 발 견 했 습 니 다. 이 인 자 는 RedisHttp Session Configuration 에 주입 되 었 습 니 다.
    @Configuration
    @EnableScheduling
    public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
            implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
            SchedulingConfigurer {
                ...
                @Bean
        public RedisOperationsSessionRepository sessionRepository() {
            RedisTemplate redisTemplate = createRedisTemplate();
            RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
                    redisTemplate);
            sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
            if (this.defaultRedisSerializer != null) {
                sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
            }
            sessionRepository
                    .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
            if (StringUtils.hasText(this.redisNamespace)) {
                sessionRepository.setRedisKeyNamespace(this.redisNamespace);
            }
            sessionRepository.setRedisFlushMode(this.redisFlushMode);
            int database = resolveDatabase();
            sessionRepository.setDatabase(database);
            return sessionRepository;
        }    
              ...
            }

    들 어 오 라 고 요청 할 때 차단 기 는 먼저 request 와 response 를 차단 한 다음 에 이 두 대상 을 Spring 내부 의 포장 류 Session Repository Request Wrapper 와 Session Repository Response Wrapper 대상 으로 변환 합 니 다.Session Repository Request Wrapper 류 는 원생 의 getSession 방법 을 다시 썼 습 니 다.코드 는 다음 과 같 습 니 다:
        @Override
            public HttpSessionWrapper getSession(boolean create) {
                 //  request getAttribue    CURRENT_SESSION  ,     
                HttpSessionWrapper currentSession = getCurrentSession();
                if (currentSession != null) {
                    return currentSession;
                }
                 //         SESSION cookie,  sessionRepository    SESSIONID Redis   Session
                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);
                          // Session   request   
                        setCurrentSession(currentSession);
                          //  Session
                        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");
                }
                 //   Session     null
                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)"));
                }
                 //  sessionRepository  RedisSession    ,           ,  
                 //@EnableRedisHttpSession      redisFlushMode     IMMEDIATE  ,   
                 //    RedisSession   Redis  。          。
                S session = SessionRepositoryFilter.this.sessionRepository.createSession();
                session.setLastAccessedTime(Instant.now());
                currentSession = new HttpSessionWrapper(session, getServletContext());
                setCurrentSession(currentSession);
                return currentSession;
            }

    Session Repository RequestWrapper 대상 의 getSession 방법 을 호출 하여 Session 을 가 져 올 때 현재 요청 한 속성 에서 먼저 찾 습 니 다. CURRENTSESSION 속성, 받 을 수 있 으 면 바로 되 돌려 줍 니 다. 이렇게 조작 하면 Redis 조작 을 감소 하고 성능 을 향상 시 킬 수 있 습 니 다.
    지금까지 redis Flushmode 가 ON 으로 설정 되면세 이브 모드 의 경우 세 션 정보 가 레 디 스에 저장 되 지 않 았 다 면 이 동기 화 작업 은 도대체 어디에서 실 행 된 것 일 까?우 리 는 Session Repository Filter 의 doFilter Internal 방법 에 마지막 으로 finally 코드 블록 이 있 는 것 을 발견 했다. 이 코드 블록 의 기능 은 Session 을 Redis 에 동기 화 하 는 것 이다.
        @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 {
                 // Session   Redis,            SESSIONID  cookie  ,       
                 //SESSION          
                wrappedRequest.commitSession();
            }
        }

    총결산
    주요 핵심 유형 은 다음 과 같다.
  • @ EnableRedisHttpSession: 세 션 공유 기능 오픈
  • RedisHttpSession Configuration: 설정 클래스, 일반적으로 우리 가 설정 할 필요 가 없습니다.주요 기능 은 Session Repository Filter 와 Redis Operations Session Repository 두 개의 Bean
  • 을 설정 하 는 것 입 니 다.
  • Session Repository Filter: 차단기
  • Redis Operations Session Repository: Redis 가 조작 하 는 클 라 이언 트 라 고 볼 수 있 습 니 다. Redis 에서 Session 을 추가 삭제 하고 검사 하 는 기능
  • 이 있 습 니 다.
  • Session Repository Request Wrapper: Request 의 포장 류 는 주로 getSession 방법
  • 을 다시 썼 습 니 다.
  • Session Repository Response Wrapper: Response 의 포장 류.

  • 원리 요약:
    요청 이 들 어 왔 을 때, Session Repository Filter 는 요청 을 먼저 차단 하고, request 와 Response 대상 을 Session Repository Request Wrapper 와 Session Repository Response Wrapper 로 변환 합 니 다.추 후 request 의 getSession 방법 을 처음 호출 할 때 Session Repository RequestWrapper 의 getSession 방법 으로 호출 됩 니 다.이 방법의 논 리 는 먼저 request 의 속성 에서 찾 는 것 입 니 다. 찾 지 못 하면;키 값 이 "SESSION" 인 쿠키 를 찾 습 니 다. 이 쿠키 를 통 해 sessionId 를 가 져 와 redis 에서 찾 습 니 다. 찾 을 수 없 으 면 RedisSession 대상 을 직접 만 들 고 Redis 에 동기 화 합 니 다. (동기 화 시 기 는 설정 에 따라)
    남 겨 진 문제
  • 언제 쿠키 를 썼 습 니까
  • 만 료 된 세 션 을 정리 하 는 기능 이 어떻게 이 루어 졌 는 지
  • 사용자 정의 HttpSessionStrategy
  • 레 퍼 런 스
  • https://www.cnblogs.com/SimpleWu/p/10118674.html
  • 좋은 웹페이지 즐겨찾기