머리말
Java 개발자에게는 J2EE 사양에서 세션을 사용하여 사용자 로그인, ID, 권한 및 상태 정보를 저장하는 것입니다. Tomcat을 웹 컨테이너로 사용하는 대부분의 개발자의 경우 Tomcat은 사용자를 표시하고 세션 정보를 관리하기 위해 세션을 어떻게 구현합니까?
요약
세션
Tomcat은 내부적으로 세션 및 httpsession과 관련된 인터페이스를 정의합니다. 클래스 상속 시스템은 그림 1에 나와 있습니다.
그림 1 세션 클래스 상속 시스템
그림 1에는 세션 상속 시스템을 추가로 나열하며 여기에 하나씩 소개됩니다.
세션 : Tomcat의 세션에 대한 기본 인터페이스 사양은이 방법을 정의합니다.
표 1 세션 인터페이스 설명
방법 | 설명하다 |
getCreationTime ()/setCreationTime (시간 : long) | 세션의 생성 시간을 얻고 설정하십시오 |
getId ()/setId (id : String) | 세션의 ID를 얻고 설정하십시오 |
getThisAccessedTime () | 마지막 요청의 시작 시간을 얻으십시오 |
getLastAccessedTime () | 마지막 요청의 완료 시간을 얻으십시오 |
getManager ()/setManager (관리자 : 관리자) | 세션 관리자를 얻고 설정하십시오 |
getMaxInactiveInterval ()/setMaxinactiveInterval (Interval : Int) | 세션 설정 사이에 최대 액세스 간격을 가져옵니다 |
getsession () | httpsession을 얻으십시오 |
isvalid ()/setValid (isvalid : boolean) | 세션의 유효한 상태를 얻고 설정하십시오 |
Access ()/endaccess () | 세션 액세스를 시작하고 종료하십시오 |
내쉬다() | 세션 만료를 설정하십시오 |
HTTPSESSION : HTTP 클라이언트와 HTTP 서버가 제공하는 세션에 대한 인터페이스 사양은 정의하는 주요 방법을 나열하고 표 2는 이러한 방법을 소개합니다.
표 2 httpsession 인터페이스 설명
방법 | 설명하다 |
getCreationTime () | 세션의 생성 시간을 얻으십시오 |
getid () | 세션의 ID를 얻으십시오 |
getLastAccessedTime () | 마지막 요청의 완료 시간을 얻으십시오 |
getservletcontext () | 현재 세션이 속한 ServletContext를 얻으십시오 |
getMaxInactiveInterval ()/setMaxinactiveInterval (Interval : Int) | 세션 설정 사이에 최대 액세스 간격을 가져옵니다 |
getAttribute (이름 : String) /setAttribute (이름 : 문자열, 값 : 개체) | 세션 스코프 속성을 얻고 설정하십시오 |
RemoveAttribute (이름 : String) | 클리어 세션 범위 속성 |
무효화 () | 세션을 무효화 하고이 세션에 바인딩 된 객체를 취소하십시오. |
Clustersession : 클러스터 배포에 따른 세션 인터페이스 사양 그림 1에는 주요 방법이 나와 있습니다.
표 3 Clustersession 인터페이스 설명
방법 | 설명하다 |
isprimarysession () | 클러스터의 주요 세션입니까? |
SetPrimarySession (부울 1 차 세션) | 클러스터 마스터 세션을 설정하십시오 |
표준 세션 : 표준 HTTP 세션 구현,이 기사는이 구현을 예로 사용합니다.
Tomcat 클러스터를 배포 할 때 클러스터의 각 노드의 세션 상태는 현재 동기화되어야합니다.
ReplicatedSession : 매번 전체 세션 객체는 클러스터의 다른 노드와 동기화되고 다른 노드는 전체 세션 객체를 업데이트합니다. 이 구현은 비교적 간단하고 편리하지만 많은 양의 유효하지 않은 정보가 전송됩니다.
deltasession : 세션에서 점진적으로 수정 된 속성을 동기화합니다. 이 방법은 점진적이므로 네트워크 I/O의 오버 헤드를 크게 줄일 수 있지만 세션 속성 작동 프로세스의 관리와 관련하여 구현은 더 복잡해집니다.
세션 관리자
Tomcat은 내부적으로 세션 관리자의 인터페이스 사양을 공식화하기위한 관리자 인터페이스를 정의합니다.
그림 2 세션 관리자의 클래스 상속 시스템
그림 2의 해당 내용을 다음과 같이 설명합니다.
관리자 : 세션 관리자가 정의한 인터페이스 사양에 대한 Tomcat, 그림 2는 관리자 인터페이스에 정의 된 주요 방법을 나열했으며 표 4는 이러한 방법의 역할을 자세히 설명합니다.
표 4 관리자 인터페이스 설명
방법 | 설명하다 |
getContainer ()/setContainer (컨테이너 : 컨테이너) | 세션 관리자와 관련된 컨테이너, 일반적으로 컨텍스트 컨테이너를 얻거나 설정합니다. |
getDistributable ()/setDistributable (분포 가능 : 부울) | 세션 관리자가 분산을 지원하는지 여부를 확인하거나 설정하십시오 |
getMaxInactiveInterval ()/setMaxinactiveInterval (Interval : Int) | 세션 관리자가 만든 세션의 최대 비활성 간격을 얻거나 설정하십시오. |
getSessionIdLength ()/setSessionIdLength (idlength : int) | 세션 관리자가 만든 세션 ID의 길이를 얻거나 설정하십시오. |
getSessionCounter ()/setSessionCounter (SessionCounter : Long) | 세션 관리자가 만든 총 세션 수를 얻거나 설정하십시오. |
getMaxActive ()/setMaxActive (maxactive : int) | 현재 활성화 된 세션의 최대 수를 얻거나 설정 |
getActiveSessions () | 현재 모든 세션을 활성화하십시오 |
getExpiredSessions ()/setexpiredsessions (만료 : long) | 현재 만료 된 세션 수를 얻거나 설정하십시오 |
getReceDedSessions ()/setReceRedSessions (거부 세션 : int) | 거부 된 세션 수를 얻거나 설정하십시오. |
getSessionMaxAliveTime ()/setSessionMaxAlivetime (SessionMaxAlivetime : int) | 만료 된 세션에서 최대 활동 시간을 가져 오거나 설정합니다. |
getSessionAverAgealiveTime ()/setSessionAverAgeAliveTime (SessionAverAgeAlivetime : int) | 만료 된 세션의 평균 활동 기간을 얻거나 설정하십시오. |
추가 (세션 : 세션)/제거 (세션 : 세션) | 세션 관리자에게 활성 세션을 추가하거나 삭제하십시오 |
changessessionID (세션 : 세션) | 세션에 새로 생성 된 랜덤 세션 ID 설정 |
Createsession (SessionId : String) | 세션 관리자의 기본 속성 구성을 기반으로 새 세션을 만듭니다. |
findsession (id : string) | SessionID 매개 변수의 고유 한 표시로 세션을 반환합니다. |
findsessions () | 세션 관리자가 관리하는 모든 활동을 반환합니다 |
load ()/unload () | 지속 메커니즘 또는 쓰기 세션에서 지속 메커니즘에서로드 세션 |
BackgroundProcess () | 컨테이너 인터페이스는 백그라운드에서 특정 컨테이너 처리 관련 작업의 구현으로 정의됩니다. |
ManagerBase : Manager 인터페이스에서 일반적으로 구현되는 추상 클래스를 캡슐화하면 Load ()/unload ()를 구현하지 않습니다. 모든 세션 관리자는 ManagerBase에서 상속됩니다.
ClusterManager : 클러스터에서 세션 관리를 구현하는 모든 관리자를 기반으로 클러스터 배포 아래 일부 인터페이스를 추가했습니다.
PersistentManagerBase : 세션 지속성의 기본 구현을 제공합니다.
PersistentManager : PersistentManagerBase에서 상속되면 Server.xml에서 <store> 요소를 구성하여 사용할 수 있습니다. PersistentManager는 메모리의 세션 정보를 파일 또는 데이터베이스로 백업 할 수 있습니다. 세션 객체가 백업되면 세션 객체는 메모리 (파일 또는 데이터베이스)에 복사되고 원래 객체는 메모리에 남아 있습니다. 따라서 서버가 다운 되더라도 활성 세션 객체를 메모리에서 검색 할 수 있습니다. 활성 세션 객체가 상한을 초과하거나 세션 객체가 너무 오랫동안 유휴 상태 인 경우 세션은 메모리로 바꾸어 메모리 공간을 절약합니다.
StandardManager : <store> 요소를 구성 할 필요가 없습니다. Tomcat이 정상적으로 닫히거나 다시 시작되거나 웹 응용 프로그램을 다시로드하면 Tomcat 디렉토리의 /work/catalina/host_name/webapp_name/sessions.ser 파일로 메모리 세션을 직렬화합니다. . Tomcat이 다시 시작되거나 응용 프로그램이로드되면 Tomcat은 파일의 세션을 메모리로 복원합니다. 서버가 갑자기 종료되면 StandardManager가 저장 처리를 구현할 기회가 없기 때문에 모든 세션이 손실됩니다.
ClusterManagerBase : 세션에 클러스터 관리 구현을 제공합니다.
Deltamanager : ClusterManagerBase에서 상속됩니다. 이 세션 관리자는 클러스터 배포에서 Tomcat의 기본 관리자입니다. 클러스터의 노드가 세션을 생성하거나 수정하면 Deltamanager는 이러한 수정 증분을 다른 노드에 복사합니다.
BackupManager : ClusterManagerBase를 상속하지는 않지만 ClusterManager 인터페이스를 직접 구현합니다. 클러스터 배치하에있는 Tomcat의 선택적 세션 관리자입니다. 클러스터의 모든 세션은 백업 노드로 완전히 복제됩니다. 클러스터의 모든 노드는이 백업 노드에 액세스하여 클러스터에서 세션의 백업 효과를 달성 할 수 있습니다.
간단하게하기 위해이 기사는 세션 관리를 설명하기위한 예제로 StandardManager를 사용합니다. StandardManager는 현재 컨텍스트의 모든 세션의 생성 및 유지 관리를 관리하는 데 사용되는 StandardContext의 자식 구성 요소입니다. "Tomcat 소스 코드 분석 - 수명주기 관리"기사의 내용을 읽거나 익숙해 져야한다면, 표준 콘텍스트가 공식적으로 시작될 때, 즉 StandardContext의 시작 방법 (목록 1 참조)이 표시됩니다. 여전히 StandardManager를 시작합니다.
코드 목록 1
@override protected synchronized void startInternal ()은 LifeCycleException을 던지려면 {// 세션 관리와 관련이없는 코드를 생략하십시오. CONTEXTMANAGER = null (manager == null) {if (getCluster ()! = null. && 배포 가능) {contextManager (). CreateManager (getName ()) {log.error ( "StandardContext.clusterFail", ex} else {ContextManager = new StandardManager (); 클러스터에 배포 된 컨텍스트가 있고 자체 관리자 getCluster (). RegisterManager (Manager)가 있음을 알리십시오. // 세션 관리와 관련이없는 코드를 생략하십시오. if (manager! = null) && (Manager Instance의 LifeCycle)) {((LifeCycle) getManager () .ERROR ( "Error Manager ()", E);
Listing 1에서 STARFINGCONTEXT의 시작 방법에서 세션 관리와 관련된 실행 단계가 다음과 같습니다.
StandardManager 만들기;
Tomcat이 분산 배포를 위해 Apache를 결합한 경우 현재 표준 관리자는 클러스터에 등록됩니다.
StandardManager를 시작하십시오.
StandardManager의 시작 방법은 StandardManager를 시작하는 데 사용되며 구현은 코드의 목록 2에 표시됩니다.
코드 목록 2
@override public synchronized final void start ()는 LifeCycleException을 던지려면 {// 상태 확인 코드를 생략합니다. state.equals (lifecyclestate.stopped) {invalidtransition (lifecycle.before_start_event); } if (state.equals (lifecyclestate.failed) || state.equals (lifecyclestate.must_stop)) {stop () {// 하위 클래스에서 수표로 작용합니다. 그들이해야 할 일을한다.
Listing 2에서 StandardManager를 시작하는 단계는 다음과 같습니다.
INIT 메소드를 호출하여 StandardManager를 초기화하십시오.
Startinternal 메소드를 호출하여 StandardManager를 시작하십시오.
StandardManager의 초기화
위의 분석 후 표준 관리자를 시작하는 첫 번째 단계는 부모 클래스 LifeCycleBase의 Init 메소드를 호출하는 것입니다. StandardManager의 초기에 관심을 갖기 위해. StandardManager 자체는 Intinternal 메소드를 구현하지 않지만 StandardManager의 상위 클래스 관리자베이스는이 방법을 구현합니다. 구현은 목록 3을 참조하십시오.
코드 목록 3
@override void initinclection ()는 LifecycleException (super.initinternal ();
코드 3의 목록을 읽으면서 ManagerBase의 Intinnal 메소드의 실행 단계를 요약합니다.
컨테이너 자체 인 StandardManager를 JMX에 등록하십시오 (Lifecyclembeanbase의 초기형 방법의 구현을 위해 "Tomcat 소스 코드 분석 - 수명주기 관리"기사를 참조하십시오);
상위 컨테이너 표준 콘텍스트에서 현재 Tomcat을 가져 와서 ManagerBase 배포 가능의 부울 속성으로 설정하십시오.
이 파일이 존재하지 않으면 wendombytes 메소드를 호출하여 임의의 바이트 배열을 얻으십시오.
참고 : 여기에서 GetRandomBytes 메소드를 호출하여 생성 된 랜덤 바이트 배열은 여기에서 호출되는 이유는 실제로 세션 ID를 할당 할 때 사용될 수 있도록 랜덤 번호 생성기의 초기화를 완료하는 이유입니다.
GetRandomBytes 메소드의 코드 구현을 자세히 읽어 보겠습니다. 코드의 목록 4를 참조하십시오.
코드 목록 4
보호 된 void getrandombytes (byte bytes []) {// (devrandomsource! = null && randomis == null) if randomfile (devrandomsource); int len = randomis.read (bytes); CATCH (ex) {// 무시하십시오} randomis.close () {rand. ) .nextbytes (바이트);
Listing 4의 SetrandomFile
메소드 (코드의 목록 5 참조)는 임의 번호 파일 /dev /urandom에서 임의의 바이트 배열을 얻는 데 사용됩니다.
코드 5 목록
public void setrandomfile (문자열 s) {// 해킹으로서 정적 파일을 사용하고 동일한 // 세션 IDS (이상한 디버깅에 적합)를 생성 할 수 있습니다. if (globals.is_se curity_enabled) {randomis = accesscontroller.doprivileged PrivileSetRandomFile (s) {devrandomsource = s (devrandomsource); readlong (); if (log.isdebugenabled ()) log.debug ( " + devrandomsource)} {log.warn ("error reading " + devrandomsource, ex); ) {randomis.close ()} log.warn (randomis를 닫습니다.
Listing 4의 SetrandomFile 메소드 (목록 6 참조)는 반사를 통해 Java.security.securerandom의 인스턴스를 생성 하고이 인스턴스를 사용하여 임의 바이트 배열을 생성합니다.
코드 목록 6
public random getrandom () {if (this.random == null) {// 새로운 랜덤 숫자 생성기 긴 시드를 계산합니다. for (int i = 0; intropy.length; i ++) {(byte) entropy [i] (i % 8) * 8); // 새로운 랜덤 숫자 생성기 클래스 <?> class.forname (randomclass); e) {// 간단한 사례로 돌아갑니다 (seed))} if (log.isdebugenabled ()) {long t2 = system.currenttimeMillis (((t2-t1))> 100) log.debug (sm.getString ( "mangustbase.seeding", randomclas s) ) + "" + (t2-t1))} return (this.random);
위의 분석에 따르면, StandardManager의 초기화는 주로 ManagerBase의 시작 방법을 실행합니다.
StandardManager의 시작
StandardManager의 시작 방법을 호출하는 것은 StandardManager를 시작하는 데 사용됩니다. 목록 7을 참조하십시오.
코드 목록 7
@override 보호 동기화 된 startinternal ()는 LifeCycleException을 던집니다. isdebugenabled () log.debug ( "while romight inginitization} catch (trashable t) {sm.get string ("StandardManager. ManagerLoad "), t);} setstate (lifecyclestate.starting);}
Listing 7에서 StandardManager를 시작하는 단계는 다음과 같습니다.
1 단계 : 새 세션 ID를 생성하려면 GeneratesESSionID 메소드 (목록 8 참조)를 호출합니다.
코드 목록 8
보호 된 STRINGSESSIONID () {byte random [] = string jvmroute = getjvmroute (); int resultlenbytes = 0; for (int j = 0; 무작위 [J] & 0x0f); (b2 <10). jvmroute) {buffer.append ( '.'). : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
2 단계는 지속적인 세션 정보를로드합니다. 세션을 지속 해야하는 이유는 무엇입니까? 모든 세션은 StandardManager의 ConcurrEthashMap에서 유지되므로 서버 재시작 또는 다운 타임으로 인해이 세션 정보가 손실되거나 유효하지 않습니다. Tomcat은 지속성을 사용하여 이러한 세션이 손실되지 않도록합니다. StandardManager의로드 메소드 구현을 살펴 보겠습니다. 코드 목록 9를 참조하십시오.
코드 목록 9
public void load ()는 classNotFoundException, ioException {if (securityUtil.ispackageProtectionEnabled ()) {accessController.dop Ivileged (new resilegedDoLoad ()} (privilegedActionException) {ex.getexception (ex.getexception); ClassNoTFoundException의 예외) {throw (classNotFoundException) 예외} else (exception of IoException) {wash (ioxception)} log.debug } else {doload ()}.
보안 메커니즘을 켜야하고 패키지 보호 모드가 켜져 있으면 코드의 10 개 목록에 표시된대로 구현되는 PRICEDEDDOLOAD를 작성하여 지속 된 세션이로드됩니다.
목록 10
개인 클래스 권한을 구현합니다. PrivilegedException <void> {provilegedDoLoad () {// noop} public void run (do load);
Listing 10에서 실제로로드를 담당하는 메소드는 DOLOAD임을 알 수 있습니다. 따라서 DOLOAD의 구현을 살펴 보면 코드 목록 11을 참조하십시오.
목록 11
보호 된 doload ()는 classNotFoundException, ioException {if (log.isdebugenabled ()) log.debug ( "START : SEREDING SERSISTED SESSIONS"); 지정된 pathName에, 파일 = 파일 (file == null) log.debug (sm.getString ( "StandardManager.load ing"); fileinputestremsprestrem inull; 컨테이너! 클래스 로더에 대한 사용자 정의 객체 입력 스트림 만들기 "); OIS = 새로운 CustomObjectInputStream (BIS, ClassLoader);} else {if (log.isdebugenabled ()) log.debug ("표준 OB JECT 입력 스트림 생성 "); OIS = 새로운 ObjectInputStream (BIS)} catch (filenotfoundException e) {log.debug ( "지속 된 데이터 파일 없음") {logception e) {sm .getString ( "StandardManager.loading .ioe", e); null) {try {bis} catch (ioexception f) {// ingore}} // 동기화 된 세션을로드합니다. readObject (); int n = count.intValue (); {표준 세션 = getNewSession (OIS); ) {// 메모리 누출을 방지하기 위해 세션이 유효합니다. getString (표준 Manager.Loading.cnfe ", E); .getString (표준 Manager.Loading. Ioe ", e); OIS.close ();} catch (ioexception f) {// resident} // if (file.exists ()) file.delete ()}; 디버그 ( "마무리 : Loa Ding이 지속 된 세션");
Listing 11에서 StandardManager Doload 메소드의 실행 단계는 다음과 같습니다.
세션 캐시로 유지 관리되는 세션 정보를 지우십시오.
다음과 같은 현재 컨텍스트에서 세션 영구 파일을 반환하려면 파일 메소드를 호출하십시오.
세션 영구 파일의 입력 스트림을 열고 CustomObjectInputStream으로 캡슐화하십시오.
세션 영구 파일에서 지속 된 세션 수를 읽은 다음 세션 정보를 하나씩 읽고 세션 캐시에 넣습니다.
이 시점에서 표준 관리자의 출시에 대한 소개는 다음 기사에서 할당, 추적, 파괴 및 기타 내용을 설명합니다.