TsgcWebSocketHTTPServer | Sesje

HTTP jest protokołem bezstanowym (przynajmniej do wersji HTTP 1.1), więc klient żąda pliku, serwer wysyła odpowiedź i połączenie jest zamykane (można włączyć keep-alive, aby połączenie nie było zamykane natychmiast, ale wykracza to poza zakres tego artykułu). Sesje umożliwiają przechowywanie informacji o kliencie, które mogą być używane na przykład podczas logowania klienta. Można użyć dowolnego unikalnego identyfikatora sesji, przeszukać listę sesji w celu sprawdzenia, czy już istnieje, a jeśli nie, utworzyć nową sesję. Sesja może zostać zniszczona po okresie bezczynności lub ręcznie po wylogowaniu klienta.

 

Konfiguracja

W komponencie TsgcWebSocketHTTPServer istnieje kilka właściwości włączających/wyłączających sesje. Najważniejsze z nich to:

 

Właściwość Opis
SessionState To pierwsza właściwość, która musi zostać włączona, aby korzystać z sesji. Bez włączenia tej właściwości sesje nie będą działać

SessionTimeout

Należy ustawić wartość większą od zera (w milisekundach) określającą maksymalny czas aktywności sesji.
AutoStartSession Sesje mogą być tworzone automatycznie (AutoStartSession = true) lub ręcznie (AutoStartSession = false). Jeśli sesje są tworzone automatycznie, serwer użyje RemoteIP jako unikalnego identyfikatora do sprawdzenia, czy istnieje aktywna sesja zapisana.
SessionClass Opcjonalne. Klasa, której serwer używa podczas tworzenia nowej sesji. Ustaw ją na własnego potomka TIdHTTPSession, aby przechowywać własne dane w każdej sesji. Musi być ustawiona przed aktywacją serwera.
SessionList Lista przechowująca aktywne sesje. Odczytaj ją, aby wyszukiwać, tworzyć lub usuwać sesje z poziomu kodu. Możesz również przypisać własną listę, jeśli potrzebujesz pełnej kontroli nad miejscem przechowywania sesji. Musi być przypisana przed aktywacją serwera.

 

 


TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;

 

Utwórz sesję

Aby utworzyć nową sesję, należy utworzyć nowy identyfikator sesji, który jest unikalny. Można użyć dowolnej wartości. Przykład: jeśli klient się uwierzytelnia, jako identyfikator sesji można użyć użytkownik + hasło + remoteip.

Następnie przeszukaj listę sesji, aby sprawdzić, czy sesja już istnieje. Jeśli nie istnieje, utwórz nową.

 

Gdy tworzona jest nowa sesja, wywoływane jest zdarzenie OnSessionStart, a gdy sesja jest zamykana, wywoływane jest zdarzenie OnSessionEnd.

 


procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; 
  AResponseInfo: TIdHTTPResponseInfo);
var
  vID: String;
  oSession: TIdHTTPSession;
begin
  if ARequestInfo.Document = '/' then
    AResponseInfo.ServeFile(AContext, 'yourpathhere\index.html')
  else
  begin
    // check if user is valid
    if not ((ARequestInfo.AuthUsername = 'user') and (ARequestInfo.AuthPassword = 'pass')) then
      AResponseInfo.AuthRealm := 'Authenticate'
    else
    begin
      // create a new session id with authentication data
      vID := ARequestInfo.AuthUsername + '_' + ARequestInfo.AuthPassword + '_' + ARequestInfo.RemoteIP;
 
      // search session
      oSession := TsgcWebSocketHTTPServer1.SessionList.GetSession(vID, ARequestInfo.RemoteIP);
 
      // create new session if not exists
      if not Assigned(oSession) then
        oSession := TsgcWebSocketHTTPServer1.SessionList.CreateSession(ARequestInfo.RemoteIP, vID);
 
      AResponseInfo.ContentText := '<html><head></head><body>Authenticated</body></html>';
      AResponseInfo.ResponseNo := 200;
    end;
  end;
end;

Odczytaj bieżącą sesję

Gdy sesja już istnieje, serwer dołącza ją do każdego żądania, które niesie plik cookie sesji. Odczytaj ją z ARequestInfo.Session, które ma wartość nil, gdy żądanie nie ma sesji.


procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
  AResponseInfo: TIdHTTPResponseInfo);
begin
  if Assigned(ARequestInfo.Session) then
  begin
    // Content is a TStrings you can use to store your own values
    ARequestInfo.Session.Content.Values['visits'] :=
      IntToStr(StrToIntDef(ARequestInfo.Session.Content.Values['visits'], 0) + 1);

    AResponseInfo.ContentText := 'Session ' + ARequestInfo.Session.SessionID +
      ' visits: ' + ARequestInfo.Session.Content.Values['visits'];
  end
  else
    AResponseInfo.ContentText := 'No session';
end;

Użyj własnej klasy sesji

Jeśli chcesz przechowywać własne pola w każdej sesji, zamiast używać listy ciągów Content, utwórz klasę potomną TIdHTTPSession i wskaż serwerowi, aby jej używał, poprzez właściwość SessionClass. Ustaw ją przed aktywacją serwera.

Serwer nadal zajmuje się wszystkim innym: generuje unikalny identyfikator sesji, wysyła plik cookie sesji, stosuje SessionTimeout i usuwa nieaktywne sesje. Nadpisz wirtualny konstruktor CreateInitialized, jeśli chcesz zainicjować własne pola podczas tworzenia sesji.


type
  TMySession = class(TIdHTTPSession)
  private
    FUserName: String;
    FLoginTime: TDateTime;
  public
    constructor CreateInitialized(AOwner: TIdHTTPCustomSessionList;
      const SessionID, RemoteIP: string); override;
    property UserName: String read FUserName write FUserName;
    property LoginTime: TDateTime read FLoginTime write FLoginTime;
  end;

constructor TMySession.CreateInitialized(AOwner: TIdHTTPCustomSessionList;
  const SessionID, RemoteIP: string);
begin
  inherited CreateInitialized(AOwner, SessionID, RemoteIP);
  FLoginTime := Now;
end;

// configure the server before it starts
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.SessionClass := TMySession;
TsgcWebSocketHTTPServer1.Active := True;

// and read it back in any request
procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
  AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Session is TMySession then
    AResponseInfo.ContentText := TMySession(ARequestInfo.Session).UserName;
end;

Użyj własnej listy sesji

Jeśli potrzebujesz pełnej kontroli nad sposobem przechowywania sesji, na przykład trzymając je w bazie danych lub współdzieląc je między kilkoma serwerami, przypisz własną listę do właściwości SessionList, przed aktywacją serwera. Wyprowadź klasę z TIdHTTPDefaultSessionList i nadpisz wirtualną metodę CreateSession. Serwer wywołuje ją wewnętrznie z CreateUniqueSession, więc unikalny identyfikator sesji nadal jest generowany za Ciebie.

Gdy przypiszesz własną SessionList, właściwość SessionClass przestaje obowiązywać, ponieważ to Twoja lista decyduje, jaką klasę utworzyć.


type
  TMySessionList = class(TIdHTTPDefaultSessionList)
  public
    function CreateSession(const RemoteIP, SessionID: string)
      : TIdHTTPSession; override;
  end;

function TMySessionList.CreateSession(const RemoteIP, SessionID: string)
  : TIdHTTPSession;
begin
  Result := TMySession.CreateInitialized(Self, SessionID, RemoteIP);
  SessionList.Add(Result);
end;

// assign it before the server starts
TsgcWebSocketHTTPServer1.SessionList := TMySessionList.Create(nil);
TsgcWebSocketHTTPServer1.Active := True;

Uwaga dotycząca OnCreateSession

Zdarzenie OnCreateSession pozwala samodzielnie zwrócić instancję sesji, ale nie przypisuje jej identyfikatora sesji. Jeśli utworzysz sesję za pomocą zwykłego konstruktora, identyfikator sesji i plik cookie sesji są puste, a sesji nigdy nie da się ponownie odnaleźć przy następnym żądaniu. Zamiast tego użyj SessionClass, które jest prostsze i wszystko to załatwia za Ciebie.

Ponadto nie wywołuj SessionList.CreateUniqueSession wewnątrz OnCreateSession. Ta metoda już dodaje nową sesję do listy, a serwer dodaje ją ponownie, gdy Twoja procedura obsługi zakończy działanie, więc ta sama sesja trafia na listę dwukrotnie.