/*
jwtTest - JSON Web Token Testimplementierung
Copyright (C) 2021 Ulrich Hilger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see
** * * Mit einem so angelegten HTML-Formular kommt die Anfrage vom Formular als * Body der HTTP POST Anfrage in folgender Form beim Server an: * *
* j_username=nutzerid&j_password=nutzerkennwort ** * Zur Authentifizierung zieht der LoginHandler einen JWTAuthenticator heran. * Der LoginHandler erwartet zu diesem Zweck ein Attribut namens * "jwtauth" im HttpContext des HttpExchange-Objekts, das auf ein Objekt der * Klasse JWTAuthenticator verweist, * * Bei erfolgreicher Authentifizierung ermittelt der LoginHandler die * urspruenglich angefragte Ressource mit Hilfe des Authenticators und * antwortet dem Client mit einer Weiterleitung an diese Ressource * (HTTP Statuscode 303 "See Other"). * * * * --- * Der nachfolgende Text ist veraltet. * * Die ursprunglich angefragte Ressource wird mit Hilfe eines Cookie namens *
JWTAuthenticator.JWT_INDICATOR
* ermittelt, der vor dem Aufruf des Login-Formulars vom JWTAuthenticator
* erzeugt wird.
*
*
* Diesen
* JWTAuthenticator verwendet der LoginHandler, um den Benutzer anzumelden.
* Mit erfolgreicher Anmeldung gibt der JWTAuthenticator einen JSON Web Token
* an den LoginHandler ab, den dieser als HttpOnly Cookie an den Browser
* weiterreicht. Nachfolgende Anfragen erfolgen dann mit diesem JWT-Cookie
* und werden vom JWTAuthenticator durchgelassen.
*
* Die Antwort vom LoginHandler muss in der Client-App nicht verarbeitet werden,
* weil der Browser ohne sonstiges zutun nachfolgenden Anfragen den JWT als
* Cookie mitgibt.
*
* JWT muessen als HttpOnly Cookie an den Browser ausgegeben werden, weil
* anderenfalls per cross site scripting (XSS) im Browser auf den JWT
* zugegriffen werden kann. Die zusaetzliche Option Secure bewirkt, dass
* der JWT nur an den Server uebermittelt wird, wenn die Verbindung
* verschluesselt ist. Auf diese Weise kann bei einer Man-in-the-middle-Attacke
* der JWT nicht ausgelesen werden.
*
* Die Verwendung von Cookies fuer JWT hat zudem den Vorteil, dass die
* Client-App keine Logik zur Verwendung des JWT besitzen muss. Der Browser
* tauscht selbsttaetig den JWT mit dem Server aus.
*
* Abwandlungen dieses LoginHandlers koennen z.B. eine JSON-Notation
* fuer Benutzer-ID und Kennwort im Body verwenden.
*
* HTTP POST /jwttest/login mit Benutzername und Kennwort im Body:
* {"name": "fred", "password": "secret"}
*
* Das kann fuer API-Authentifizierungen nutzlich sein. Dazu passend wird auf
* dem Client wie folgt vorgegangen.
*
* Benutzername und Kennwort als HTTP POST an /jwttest/login senden
* var sendObject = JSON.stringify({name: user, password: password});
*
*
* @author Ulrich Hilger
* @version 1, 21. Mai 2021
*/
public class FormLoginHandler extends LoginHandler {
private static final Logger logger = Logger.getLogger(FormLoginHandler.class.getName());
public static final String FORM_NUTZER_NAME = "j_username";
public static final String FORM_KENNWORT = "j_password";
public static final String HEADER_LOCATION = "Location";
public static final String RESP_SEE_OTHER = "See Other";
public FormLoginHandler() {
//this.ctx = ctx;
}
@Override
protected void loginResponse(HttpExchange exchange, Authenticator auth, String token) throws IOException {
if(auth instanceof FormAuthenticator) {
setAuthenticatedHeader(exchange, auth, token);
FormAuthenticator formAuth = (FormAuthenticator) auth;
// Weiterleitungsziel bestimmen und in den Antwort-Kopf schreiben
String wert = formAuth.cookieLesen(exchange, TokenAuthenticator.JWT_INDICATOR);
logger.info(wert);
String sessionReferer = formAuth.getReferer(wert);
Headers respHeaders = exchange.getResponseHeaders();
respHeaders.add(HEADER_LOCATION, sessionReferer);
// Antwort senden
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
// 303 See Other erlaubt eine Weiterleitung
// mit Wechsel von POST (login) zu GET (urspruengliche Ressource)
HttpResponder responder = new HttpResponder();
responder.antwortSenden(exchange, 303, RESP_SEE_OTHER);
} else {
throw new IOException("Wrong authenticator type, should have been FormAuthenticator.");
}
}
@Override
protected User getUser(HttpExchange exchange) throws IOException {
String body = bodyLesen(exchange);
String[] nameWertPaare = body.split(TokenAuthenticator.STR_AMP);
HashMap