Czy istnieje sposób, aby otrzymać listę wszystkich metod oznaczonych Since: Servlet 3.0 w Javadoc?
Do rozpoznania tematu korzystam bezpośrednio ze specyfikacji JSR-315 Java Servlet 3.0 i zacznę od dwóch nowych metod w javax.servlet.http.HttpServletRequest - login and logout. Specyfikacja poświęca im osobny rozdział 13.10 Login and Logout (strona 162).
Instancja
HttpServletRequest jest nam (programistom) dana w trakcie obsługi żądania przez kontener. Zawiera informacje z nagłówka (ang. header) i treści (ang. body) żądania HTTP. Parametry są typu String, a wraz z wartościami tworzą zbiór par (parametr, wartość) - mapę. Jak mi to doskonale wpasowuje się w programowanie z funkcyjnym Clojure, w którym struktury są niezmienne (jak HttpServletRequest) i dostęp do nich jest jakby przyjemniejszy (wybacz, nie mogłem się oprzeć, aby tego tutaj nie wtrącić).Ciekawostka: Dla danego parametru może istnieć wiele wartości, w postaci tablicy. Dostęp do nich odbywa się za pomocą getParameterValues. Istnieje również getParameter, który zwraca pojedynczą wartość. Pytanie certyfikacyjne mogłoby być: "Który w stosunku do tablicy zwracanej przez getParameterValues?" Pierwszy z pewnością, ale czy pierwszy jest pojęciem trwałym i wielokrotne wykonanie
getParameter zawsze zwróci tą samą wartość? Specyfikacja (strona 43) wymusza, aby był to pierwszy element tablicy zwracanej przez getParameterValues i zawsze ten sam. Kolejność wyznaczana jest przez "query string", aby dołączyć do nich te z treści żądania (strona 44).Ciekawostek cd: Pliki w META-INF/resources w pliku JAR są dostępne jedynie w sytuacji, kiedy kontener rozpakował je przy wywołaniu metody ServletContext.getRealPath() (strona 48). Nie wiedziałem o istnieniu tego specjalnego katalogu (!)
Wracając do
HttpServletRequest, login i logout.Przy obsłudze żądania serwer aplikacyjny określa tożsamość nadawcy. Jest ona niezmienna, aż do momentu poprawnego wywołania metod
authenticate, login lub logout na tym żądaniu (strona 162). Metody getUserPrincipal() oraz getRemoteUser() zwrócą null dla sytuacji, w których nadawca żądania nie został uwierzytelniony (strona 163). Później jeszcze pojawia się metoda getAuthType, która również zwróci null.Serwer (w postaci kontenera servletów/webowego) może stworzyć sesję HTTP na potrzeby utrzymywania stanu uwierzytelnienia użytkownika. Jeśli utworzy ją programista przed uwierzytelnieniem użytkownika, to po poprawnym uwierzytelnieniu sesja musi być taka sama jak przed (strona 163). W ten sposób możliwe jest utrzymanie informacji w pojedynczej sesji przed, w trakcie i po uwierzytelnieniu nadawcy (użytkownika).
W javadoc dla HttpServletRequest.html#login(java.lang.String, java.lang.String) można przeczytać:
Wywołanie metody
login uruchamia proces uwierzytelnienia zgodnie z konfiguracją ServletContext (cóż za magia, a pewnie chodzi o konfigurację aplikacji webowej przez web.xml czy adnotacje). Metoda nie spowoduje rzucenia wyjątkiem ServletException, jeśli mechanizm uwierzytelniający wspiera sprawdzenie pary (login, hasło), nadawca nie został jeszcze określony - wszystkie metody getUserPrincipal, getRemoteUser oraz getAuthType zwracają null oraz para (login, hasło) jest poprawna.Poprawne wykonanie metody sprawia, że metody
getUserPrincipal, getRemoteUser oraz getAuthType zwracają wartość nie-null, tj. dane nadawcy.Proste i przyjemne, co? Za wyjątkiem konfiguracji bezpieczeństwa, tj. uwierzytelnienia i autoryzacji przez kontener, co wcale nie musi być zadaniem trywialnym. Mimo uproszczeń w konfiguracji w serwerach aplikacyjnych, to wciąż niezbyt popularny aspekt zarządzania nimi.
Ja skorzystam z przykładu uruchomionego na IBM WebSphere Application Server 8.5.0.1.
package pl.japila.javaee6;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Principal;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String HTML_BR = "<b r>"; // for Blogger only
private void displayCredentials(PrintWriter out, HttpServletRequest request) {
Principal principal = request.getUserPrincipal();
String user = request.getRemoteUser();
out.printf("Principal: %s%s", principal, HTML_BR);
out.printf("User: %s%s", user, HTML_BR);
out.printf("AuthType: %s%s", request.getAuthType(), HTML_BR);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.printf("Step 1. nulls only (no user authenticated)
");
displayCredentials(out, request);
out.printf("Step 2. Exception for jacek/password (no user 'jacek' available)
");
try {
request.login("jacek", "password");
} catch (ServletException expected) {
out.printf("Exception (expected): %s%s", expected, HTML_BR);
}
displayCredentials(out, request);
out.printf("Step 3. Authentication of admin/admin...");
try {
request.login("admin", "admin");
} catch (ServletException unexpected) {
out.printf("FAILED
");
out.printf("Exception (UNexpected): %s%s", unexpected, HTML_BR);
}
out.printf("SUCCESS");
displayCredentials(out, request);
out.printf("Step 4. Logging out...");
try {
request.logout();
} catch (ServletException unexpected) {
out.printf("FAILED
");
out.printf("Exception (UNexpected): %s%s", unexpected, HTML_BR);
}
out.printf("SUCCESS");
displayCredentials(out, request);
}
}Wykonanie powyższego servletu, jakkolwiek poprawne od strony użytkownika, to w dzienniku można zauważyć wyjątek SECJ0369E: Authentication failed when using LTPA. The exception is com.ibm.websphere.wim.exception.PasswordCheckFailedException: CWWIM4537E No principal is found from the 'jacek' principal name. Stąd opakowanie wywołania request.login("jacek", "password") dla nieistniejącego użytkownika w blok try-catch.