Ausführliche Erklärung zu Sitzung und Cookie in Tomcat

Ausführliche Erklärung zu Sitzung und Cookie in Tomcat

Vorwort

HTTP ist ein zustandsloses Kommunikationsprotokoll. Die einzelnen Anfragen sind unabhängig voneinander und der Server kann vorherige Anfragen nicht identifizieren. Bei Webanwendungen sind alle Aktivitäten von einem bestimmten Status abhängig, beispielsweise von der Benutzeranmeldung. Zurzeit erfordert die Verwendung von HTTP die Möglichkeit, nach einer Anmeldeanforderung Anmeldeinformationen für nachfolgende Anfragen bereitzustellen. Dieser Artikel wurde zuerst auf dem öffentlichen Konto Dunwu Source Code veröffentlicht.

Die Lösung besteht in der Verwendung von Cookies, die vom Server an den Browser zurückgegeben werden, der die Cookie-Daten zwischenspeichert und bei jeder Anforderung an den Server übermittelt. Cookies werden in Anfragen im Klartext übertragen und sind auf eine Größe von 4 KB begrenzt. Offensichtlich ist es unzuverlässig, alle Statusdaten im Browser zu speichern. Der gängige Ansatz ist:

  1. Bei der ersten Anfrage des Browsers weist der Server dem Benutzer eine eindeutige Kennung zu, die zurückgegeben und in den Cookies des Browsers gespeichert wird.
  2. Der Server verwaltet eine globale Anforderungsstatusbibliothek und verwendet eine generierte eindeutige Kennung, um die Statusinformationen jeder Anforderung zuzuordnen.
  3. Nachfolgende Anfragen des Browsers übermitteln die eindeutige Kennung an den Server, um Statusinformationen zur vorherigen Anfrage zu erhalten.

Zur Vereinfachung der Verwaltung bezeichnet der Server den gesamten Prozess als Sitzung und abstrahiert ihn in eine Sitzungsklasse, die zum Identifizieren und Speichern von Informationen oder Statusinformationen über den Benutzer verwendet wird.
Als Nächstes analysieren wir die Quellcodeimplementierung von Tomcat mit Version 6.0.53, und zwar durch das Parsen und Generieren von Sitzungskennungen sowie das Erstellen, Löschen und Persistentmachen von Sitzungen.

1. Lösen Sie die Sitzungskennung auf

Cookies sind der am häufigsten verwendete Mechanismus zur Sitzungsverfolgung. Alle Servlet-Container, einschließlich Tomcat, unterstützen sie. In Tomcat lautet der Standardname des Cookies, das die Sitzungskennung speichert, JSESSIONID.

Wenn Ihr Browser keine Cookies unterstützt, können Sie zur Erfassung von Kennungen auch folgende Methode verwenden:

  • URL-Umschreibung: In der URL als Pfadparameter enthalten, beispielsweise /path;JSESSIONID=xxx
  • URL-Anforderungsparameter: Fügen Sie die eindeutige Sitzungskennung als Abfrageparameter zu allen Links auf der Seite hinzu, z. B. /Pfad?JSESSIONID=xxx
  • FORM verstecktes Feld: Ein verstecktes Feld wird im Formular verwendet, um einen eindeutigen Wert zu speichern und wird mit dem Formular an den Server übermittelt.

Tomcat implementiert die Extraktion von JSESSIONID aus dem URL-Umschreibungspfad und Cookie. Bevor wir den Quellcode analysieren, schauen wir uns zunächst die wichtigsten Informationen der Header-Felder der Antwort zum Setzen von Cookies und der Anfrage mit Cookies an:

// Cookie setzen
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=56AE5B92C272EA4F5E0FBFEFE6936C91; Pfad=/Beispiele
Datum: So, 12. Mai 2019 01:40:35 GMT

// Cookie absenden
GET /Beispiele/Servlets/Servlet/SessionExample HTTP/1.1
Host: localhost:8080
Kekse: JSESSIONID=56AE5B92C272EA4F5E0FBFEFE6936C91

1.1 Pfade aus URLs umschreiben

Eine URL, die einen Sitzungs-ID-Pfadparameter enthält, sieht wie folgt aus:

http://localhost:8080/examples/SessionExample;JSESSIONID=1234;n=v/?x=x

Einfach ausgedrückt geht es darum, die JSESSIONID zwischen dem passenden Semikolon und dem letzten Schrägstrich zu finden, was tatsächlich der Fall ist, außer dass Tomcat mit Bytes arbeitet. Der Kerncode befindet sich in der Methode CoyoteAdapter.parsePathParameters(), die hier nicht veröffentlicht wird.

1.2 Aus dem Cookie-Header

Der Methodenaufruf, der die Cookie-Analyse auslöst, lautet wie folgt:

CoyoteAdapter.service(Anfrage, Antwort)
└─CoyoteAdapter.postParseRequest(Anfrage, Anfrage, Antwort, Antwort)
 └─CoyoteAdapter.parseSessionCookiesId(Anfrage, Anfrage)
 └─Cookies.getCookieCount()
 └─Cookies.processCookies(MimeHeaders)
 └─Cookies.processCookieHeader(byte[], int, int)

Dieser processCookieHeader arbeitet mit Bytes und die Analyse scheint nicht intuitiv. Es gibt auch eine Methode, die in Tomcat als veraltet markiert ist und die Zeichenfolgenanalyse verwendet, um sie leichter verständlich zu machen. Der Code lautet wie folgt:

private void processCookieHeader(String cookieString){
 // Mehrere Cookie-Werte werden durch Kommas getrennt StringTokenizer tok = new StringTokenizer(cookieString, ";", false);
 während (tok.hasMoreTokens()) {
  Zeichenfolgen-Token = tok.nextToken();
  // Position des Gleichheitszeichens abrufen int i = token.indexOf("=");
  wenn (i > -1) {
   // Name und Wert abrufen und Leerzeichen entfernen String name = token.substring(0, i).trim();
   Zeichenfolgenwert = token.substring(i+1, token.length()).trim();
   // RFC 2109 und Fehler, entfernen Sie doppelte Anführungszeichen an beiden Enden"
   Wert = stripQuote(Wert);
   // Holen Sie ein ServerCookie-Objekt aus dem internen Cookie-Cache-Pool ServerCookie cookie = addCookie();
   // Name und Wert festlegen
   Cookie.getName().setString(name);
   Cookie.getValue().setString(Wert);
  } anders {
   // wir haben einen schlechten Keks... lass ihn einfach weg
  }
 }
}

Nach dem Parsen besteht der nächste Schritt darin, das Cookie mit dem Namen JSESSIONID in der Methode parseSessionCookiesId zu durchlaufen und zu versuchen, es abzugleichen. Wenn es vorhanden ist, wird sein Wert auf die requestedSessionId der Anforderung gesetzt und einem internen Sitzungsobjekt zugeordnet.

2. Session-Cookies generieren

Die mit der Sitzung verbundenen Cookies werden von Tomcat selbst generiert. Wenn Request.getSession() im Servlet verwendet wird, um das Sitzungsobjekt abzurufen, wird die Ausführung ausgelöst. Der Kerncode lautet:

geschützte Sitzung doGetSession(boolean create) {
 ...
 // Eine Sitzungsinstanz erstellen if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) {
  // Wenn die Sitzungs-ID von einem Cookie stammt, verwenden Sie sie erneut. Wenn sie von einer URL stammt, verwenden Sie sie nicht erneut, um mögliche Phishing-Angriffe zu verhindern. session = manager.createSession(getRequestedSessionId());
 } anders {
  Sitzung = Manager.createSession(null);
 }
 //Erstelle ein neues Session-Cookie basierend auf der Session
 wenn ((Sitzung != null) und (getContext() != null)
    && getContext().getCookies()) {
  Zeichenfolge scName = Kontext.getSessionCookieName();
  if (scName == null) {
   //Standard-JSESSIONID
   scName = Globals.SESSION_COOKIE_NAME;
  }
  //Erstelle ein neues Cookie
  Cookie cookie = neues Cookie(scName, session.getIdInternal());
  // Pfaddomäne sicher setzen
  Konfigurieren Sie SessionCookie (Cookie);
  // Zum Antwortheaderfeld hinzufügen response.addSessionCookieInternal(cookie, context.getUseHttpOnly());
 }
 wenn (Sitzung != null) {
  Sitzung.Zugriff();
  Rückkehr (Sitzung);
 } anders {
  Rückgabe (null);
 }
}

Es wird dem Antwortheaderfeld hinzugefügt und entsprechend dem Cookie-Objekt im eingangs beschriebenen Format generiert.

3. Sitzung

Session ist eine Schnittstelle innerhalb von Tomcat und eine Fassadenklasse von HttpSession, die verwendet wird, um Statusinformationen zwischen Anfragen bestimmter Benutzer einer Webanwendung aufrechtzuerhalten. Der entsprechende Klassendiagrammentwurf sieht wie folgt aus:

Die Funktionen der wichtigsten Klassen bzw. Schnittstellen sind wie folgt:

  • Manager – verwaltet den Sitzungspool. Verschiedene Implementierungen bieten spezifische Funktionen, wie Persistenz und Verteilung.
  • ManagerBase - implementiert einige grundlegende Funktionen, wie z. B. Sitzungspool, Algorithmus zur Generierung eindeutiger IDs und ist leicht zu übernehmen und zu erweitern
  • StandardManager – Standardimplementierung, die eine einfache Sitzungspersistenz über Neustarts dieser Komponente hinweg gewährleistet (beispielsweise wenn der gesamte Server heruntergefahren und neu gestartet wird oder wenn eine bestimmte Webanwendung neu geladen wird)
  • PersistentManagerBase - bietet eine Vielzahl verschiedener Methoden zur Verwaltung persistenter Speicher, wie Dateien und Datenbanken
  • Store – ermöglicht die dauerhafte Speicherung und das Laden von Sitzungs- und Benutzerinformationen
  • ClusterManager – Schnittstelle zur Cluster-Sitzungsverwaltung, verantwortlich für die Sitzungsreplikation
  • DeltaManager - repliziert Sitzungsdaten inkrementell an alle Mitglieder im Cluster
  • BackupManager - repliziert Daten auf nur einen Backup-Knoten, sichtbar für alle Mitglieder des Clusters

Dieser Artikel analysiert nicht die Prinzipien der Cluster-Replikation, sondern nur die Verwaltung eigenständiger Sitzungen.

3.1 Sitzung erstellen

Wenn Sie Request.getSession() zum ersten Mal verwenden, um ein Sitzungsobjekt in einem Servlet abzurufen, wird eine StandardSession-Instanz erstellt:

öffentliche Sitzung erstellenSession(String sessionId) {
 // Die Standardrückgabe ist eine neue StandardSession(diese)-Instanz. Session session = createEmptySession();
 // Eigenschaften initialisieren session.setNew(true);
 Sitzung.setValid(true);
 Sitzung.setCreationTime(System.currentTimeMillis());
 // Legen Sie die Gültigkeitsdauer der Sitzung in Sekunden fest. Der Standardwert beträgt 30 Minuten. Ein negativer Wert bedeutet, dass die Sitzung nie abläuft. session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
 wenn (Sitzungs-ID == null) {
  // Eine Session-ID generieren
  Sitzungs-ID = generateSessionId();
 
 Sitzung.setId(Sitzungs-ID);
 Sitzungszähler++;

 SessionTiming-Timing = neues SessionTiming (session.getCreationTime (), 0);
 synchronisiert (sessionCreationTiming) {
  sessionCreationTiming.add(Zeitpunkt);
  Sitzungserstellungstiming.poll();
 }
 Rückkehr (Sitzung);
}

Der Schlüssel liegt in der Generierung der eindeutigen Sitzungskennung. Sehen wir uns den Generierungsalgorithmus von Tomcat an:

  1. Holen Sie sich 16 Bytes zufällig
  2. Verschlüsseln Sie diese Bytes mit MD5, so dass Sie wieder ein 16-Byte-Array erhalten.
  3. Durchlaufen Sie das neue Byte-Array und verwenden Sie die höchsten und niedrigsten 4 Bits jedes Bytes, um ein hexadezimales Zeichen zu generieren
  4. Schließlich erhalten wir eine 32-Bit-Hexadezimalzeichenfolge.

Der Kerncode lautet wie folgt:

geschützter String generateSessionId() {
 byte random[] = neues byte[16];
 : Zeichenfolge jvmRoute = getJvmRoute();
 String-Ergebnis = null;
 // Das Ergebnis als Zeichenfolge mit hexadezimalen Zahlen rendern StringBuffer buffer = new StringBuffer();
 Tun {
  int resultLenBytes = 0;
  if (Ergebnis != null) { // Wiederholen, Puffer neu generieren = neuer StringBuffer();
   Duplikate++;
  }
  //sessionIdLength ist 16
  während (resultLenBytes < this.sessionIdLength) {
   getRandomBytes(random); // 16 Bytes nach dem Zufallsprinzip abrufen // Zusammenfassung dieser 16 Bytes abrufen, standardmäßig mit MD5
   random = getDigest().digest(random);
   // Durchlaufe dieses Byte-Array und erzeuge schließlich einen 32-Bit-Hexadezimal-String für (int j = 0;
   j < zufällige Länge und ErgebnislängeBytes < diese Sitzungs-ID-Länge;
   j++) {
    // Erzeuge ein hexadezimales Zeichen unter Verwendung der höchsten und niedrigsten 4 Bits des angegebenen Bytes Byte b1 = (Byte) ((random[j] & 0xf0) >> 4);
    Byte b2 = (Byte) (zufällig[j] & 0x0f);
    // In hexadezimale Ziffern umwandeln, wenn (b1 < 10) {buffer.append((char) ('0' + b1));}
    // Hexadezimale Zeichen in Großbuchstaben umwandeln, sonst {buffer.append((char) ('A' + (b1 - 10)));}
    
    wenn (b2 < 10) {Puffer.Anhängen((char) ('0' + b2));}
    sonst {buffer.append((char) ('A' + (b2 - 10)));}
    ErgebnisLenBytes++;
   }
  }
  if (jvmRoute != null) {buffer.append('.').append(jvmRoute);}
  Ergebnis = Puffer.toString();
 } während (sessions.containsKey(Ergebnis));
 Rückgabe (Ergebnis);
}

3.2 Überprüfung des Sitzungsablaufs

Eine Webanwendung entspricht einem Sitzungsmanager, was bedeutet, dass sich innerhalb von StandardContext eine Managerinstanz befindet. Jede Containerkomponente startet einen Hintergrundthread und ruft regelmäßig die Methode backgroundProcess() von sich selbst und ihren internen Komponenten auf. Die Hintergrundverarbeitung des Managers dient dazu, zu prüfen, ob die Sitzung abgelaufen ist.

Die Logik der Prüfung besteht darin, alle Sitzungen abzurufen und anhand ihres isValid zu bestimmen, ob sie abgelaufen sind. Der Code lautet wie folgt:

öffentliches Boolean ist gültig() {
 ...
 // Ob geprüft werden soll, ob es aktiv ist, der Standardwert ist „false“
 wenn (ACTIVITY_CHECK && accessCount.get() > 0) {
  gibt true zurück;
 }
 // Prüfen ob die Zeit abgelaufen ist if (maxInactiveInterval >= 0) { 
  lange ZeitJetzt = System.currentTimeMillis();
  int timeIdle = (int) ((Jetztzeit - dieseZugriffszeit) / 1000L);
  wenn (Leerlaufzeit >= maxInaktivesIntervall) {
   // Wenn abgelaufen, führe eine interne Verarbeitung durch // Benachrichtige vor allem Listener, die an Ablaufereignissen interessiert sind
   ablaufen(wahr);
  }
 } // Pluralzahlen verfallen nie return (this.isValid);
}

3.3 Sitzungspersistenz

Persistenz bedeutet, die aktiven Sitzungsobjekte im Speicher in eine Datei zu serialisieren oder in einer Datenbank zu speichern. Wenn die Sitzungsverwaltungskomponente die Anforderungen erfüllt und die Persistenz aktiviert hat, erfolgt die Speicherung in ihrer Lebenszyklusereignis-Stoppmethode; das Laden erfolgt in der Startmethode.

Persistenz in Datei. StandardManager bietet auch die Funktion der Persistenz in Datei. Es schreibt alle aktiven Sitzungen im Sitzungspool in die Datei CATALINA_HOME/work/Catalina/<host>/<webapp>/SESSIONS.ser. Der Code befindet sich in seiner doUnload-Methode.

FileStore bietet auch die Funktion zum persistenten Speichern von Dateien. Der Unterschied zu StandardManager besteht darin, dass jede Sitzung in eine einzelne Datei mit dem Namen <id>.session geschrieben wird.

Bleiben Sie in der Datenbank und speichern Sie sitzungsbezogene Daten in einer Tabelle, einschließlich serialisierter Binärdaten. Die Tabellenfeldinformationen lauten wie folgt:

Tabelle tomcat_sessions erstellen (
 session_id varchar(100) nicht null Primärschlüssel,
 valid_session char(1) not null, -- Gültigkeit max_inactive int not null, -- Maximale Gültigkeitsdauer last_access bigint not null, -- Letzter Zugriffszeitpunkt app_name varchar(255), -- Anwendungsname im Format /Engine/Host/Context
 session_data mediumblob, -- binäre Daten KEY kapp_name (app_name)
);

Hinweis: Sie müssen die JAR-Datei des Datenbanktreibers im Verzeichnis $CATALINA_HOME/lib ablegen, um sie für den Klassenlader in Tomcat sichtbar zu machen.

4. Zusammenfassung

Dieser Artikel analysiert kurz die Sitzungsverwaltung von Tomcat. Natürlich werden dabei viele Details außer Acht gelassen. Interessierte können sich den Quellcode genauer ansehen. Die Implementierung der Tomcat-Clustersitzung wird später analysiert.

Zusammenfassen

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM.

Das könnte Sie auch interessieren:
  • Tomcat implementiert Sitzungsfreigabe (Sitzungsreplikation)
  • Einführung in Tomcat-Cluster und Sitzungsreplikationsanwendung
  • Detaillierte Analyse der TomCat-Sitzungsverwaltung
  • nginx+tomcat implementiert Lastenausgleich und verwendet Redis-Sitzungsfreigabe
  • Sitzungsverwaltungsmechanismus in Tomcat
  • Lösung für das Problem der gemeinsamen Synchronisierung von Tomcat-Memecached-Sitzungen in Java
  • Zusammenfassung der Sitzungsimplementierung in Tomcat
  • Eine kurze Diskussion zur Tomcat-Sitzungsverwaltungsanalyse
  • Implementierung der Sitzungsverwaltung mit Nginx+Tomcat
  • So überwachen und löschen Sie abgelaufene Sitzungen in Tomcat

<<:  Lösen Sie die Probleme, die bei der Installation von mysql-8.0.11-winx64 in einer Windows-Umgebung auftreten

>>:  JavaScript implementiert die asynchrone Erfassung von Formulardaten

Artikel empfehlen

Fünf Möglichkeiten zum automatischen Seitensprung in HTML

Im vorherigen Artikel haben wir drei gängige Meth...

Sieben verschiedene Farbschemata für das Website-Design-Erlebnis

Die Farbabstimmung beim Erstellen einer Website i...

Implementierung von webpack-dev-server zum Erstellen eines lokalen Servers

Inhaltsverzeichnis Vorwort Webpack-Deb-Server Sta...

So verwenden Sie das JavaScript-Strategiemuster zum Validieren von Formularen

Inhaltsverzeichnis Überblick Formularvalidierung ...

Aufbau einer Zookeeper-Standalone-Umgebung und einer Clusterumgebung

1. Aufbau einer Einzelmaschinenumgebung# 1.1 Heru...

So konvertieren Sie ein JavaScript-Array in eine Baumstruktur

1. Nachfrage Das Backend stellt solche Daten bere...

Kleines Programm zur Implementierung eines einfachen Taschenrechners

In diesem Artikelbeispiel wird der spezifische Co...

Was ist SSH-Portweiterleitung? Was nützt das?

Inhaltsverzeichnis Vorwort 1. Lokale Portweiterle...

Teilen Sie JS vier lustige Hacker-Hintergrundeffektcodes

Inhaltsverzeichnis Beispiel 1 Beispiel 2 Beispiel...