Bye Bye Basic Auth Popup für Java EE

Die wohl einfachste Methode eine Java EE Anwendung ab zu sichern ist durch eine Basic Authentifikation die durch einen entsprechenden Eintrag in der web.xml aktiviert wird. Anfragen ohne berechtigen Benutzer führen dann in der Antwort zu einem 401 Fehler der den Browser dazu veranlasst einen Dialog an zu zeigen der um die Eingabe der Benutzerdaten bittet. Doch was wenn wir diesen Dialog unterbinden wollen und stattdessen z.B. eine eigene Möglichkeit bieten wollen einen Login durch zu führen?

Ein einfach gehaltenes Beispiel zur Basic Authentifizierung

web.xml:

 <security-constraint>
    <web-resource-collection>
        <web-resource-name>webresources</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
        <role-name>customer</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>ApplicationRealm</realm-name>
</login-config>
<security-role>
    <role-name>admin</role-name>
</security-role>
<security-role>
    <role-name>customer</role-name>
</security-role>

Eine solche Definition hat zur Folge, dass der angesprochene Dialog vom Browser angezeigt wird:

basicauth_login2

Und hier fangen die Probleme an. Der Browser selbst erkennt den Status (401) und prüft auf das Vorhandensein eines WWW-Authenticate-Headers. Ist dies der Fall zeigt der Browser den dargestellten Dialog an. Auf Clientseite, z.B. einer AngularJS Anwendung, hat der Entwickler keinerlei Chance auf einen solchen Fehler zu reagieren da die Response gar nicht zum aufrufenden Code weiter gegeben wird. Die Lösung des Problems besteht darin auf Seite des Servers sicher zu stellen das in einem solchen Fehlerfall ein anderer Statuscode geliefert wird als der Standard es vorsieht. Wer hier an einen einfachen Servlet-Filter denkt liegt leider falsch, da auch ein Servlet-Filter in einem solchen Fall auf Server Seite nicht angesprochen wird. Trotzdem bietet uns der Java EE Standard eine einfache Möglichkeit auf 401-Fehler zu reagieren

error-page

Die web.xml unserer Anwendung bietet uns die Möglichkeit eigene Fehlerseiten zu definieren um auf spezifische Fehler zu reagieren (basierend auf Statuscode oder Excption-Typ). Dieser Konfigurationswert darf jedoch nicht nur eine statische Seite als Wert annehmen sondern auch ein Servlet. Das machen wir uns zu Nutzen um 401 Fehler auf einen entsprechend eigens definieren Statuscode zu setzen.

<error-page>
    <error-code>401</error-code>
    <location>/AccessExceptionHandler</location>
</error-page>

AccessExceptionHandler:

@WebServlet("/AccessExceptionHandler")
public class AccessExceptionHandler extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processError(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processError(request, response);
    }

    private void processError(HttpServletRequest request, HttpServletResponse response) throws IOException {
         response.setContentType("text/html");
         response.setStatus(455);
         PrintWriter out = response.getWriter();
         out.write("User not allowed to perform specified Method");
    }
}

Der hier gewähle Status-Code „455“ ist beliebig Wählbar, jedoch sollte darauf geachtet werden keine bereits im Standard definierten Code zu verwenden (s. HTTP-Spec-StatusCodes ).

Security-Regel auf Service – Ebene

Im obigen Beispiel legen wir unsere Security-Richtlinien auf konkrete Pfade innerhalb unserer Webanwendung. Denkbar wäre jedoch auch auf diese Deklaration zu verzichten und lediglich auf der Ebene unserer Services mit entsprechenden Annotationen zu arbeiten die für eine Benutzer / Rollen Prüfung sorgt.

Beispiel einer Service-EJB (die wiederrum über eine Rest-Ressource verwendet wird):

@RolesAllowed({"admin"})
public Customer updateCustomer(Customer customer)
{
    return customerRepostory.updateCustomer(customer);
}

Dieses Vorgehen hat einen entscheidenden Vorteil: Sowohl jegliche Rest-Zugriffe als auch Serverseitige Aufrufe von z.B. JSF, werden zentral durch diese Annotationen in der Berechtigung geprüft. Aufrufe mittels eines nicht berechtigten Users (oder keinem) führen im Falle eines Fehlers zu einer javax.ejb.EJBAccessException. Zusätzlich zu der obigen Definition eines Servlets als Error-Page können wir  für unsere Rest-Schnittstelle  diesen Fehler mittels eines Exception Mappers abhandeln (ohne diesen würde die Fehler mit einem Statu-Code 500 zurückgewiesen werden):

package de.gedoplan.jaxgui.system.security;

import javax.ejb.EJBAccessException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class AccessExceptionHandler implements ExceptionMapper<EJBAccessException> {

    @Override
    public Response toResponse(EJBAccessException exception) {
        return Response.status(455).type("text/html").build();
    }
}

Ergebnis:

basicauth_455

Zugegeben, das Verhindern des Login-Popups ist komplizierter als gedacht. Dennoch bietet auch der Java Standard für diesen Fall entsprechende Mechanismen um in die Verarbeitung ein zu greifen. Der umgeschriebene Fehlercode wird in diesem Beispiel als „normale“ Fehlerresponse an die Anwendung weiter gegeben sodass individuell darauf regiert werden kann.

Advertisements

Über Dominik Mathmann
Dominik Mathmann arbeitet als Berater, Entwickler und Trainer bei der GEDOPLAN GmbH. Auf Basis seiner langjährigen Erfahrung in der Implementierung von Java-EE-Anwendungen leitet er Seminare, hält Vorträge und unterstützt Kunden bei der Konzeption und Realisierung von Webanwendungen vom Backend bis zum Frontend. Sein derzeitiger Schwerpunkt liegt dabei auf der Entwicklung von JSF-Anwendungen. Er sieht die stärker werdende Position von JavaScript-Frameworks jedoch positiv und beschäftigt sich darüber hinaus mit Webframeworks wie AngularJS.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: