Deklarative Security in JSE Web Service Consumern


Blog vom

Deklarative Security wird im Bereich von Enterprise Applikationen immer wichtiger. Viele IT-Architekten und Entwickler wissen aber nicht, dass diese für Web Service Consumer nicht nur in Applikationsservern sondern auch in gewöhnlichen Java SE Applikationen verwendet werden kann. Dieser Blogbeitrag zeigt anhand eines Beispiels einen möglichen Ansatz.

Deklarative Security

Entwicklung und Betrieb moderner Enterprise Applikationen fordern Gemeinschaftseffort verschiedenster Spezialisten mit unterschiedlichsten Skills. Um der einhergehenden Komplexität entgegenzutreten, hat sich in der Software Entwicklung das Prinzip des Separation of Concern durchgesetzt.

Security kann in diesem Sinn ebenfalls als „Concern“ oder Aspekt betrachtet werden. Dabei will man Security-relevante Funktionalität nicht im Gesamtsystem verteilt, sondern möglichst zentral und entkoppelt von der Geschäftslogik halten. Wird dies ausprogrammiert, spricht man von imperativer Security. Die zunehmende Verteilung der Systeme und deren Heterogenität bringen neue Herausforderungen und verschärfen existierende Anforderungen an die Security. Das ist sicher einer der Gründe, warum deklarative Security in den letzten Jahren Aufschwung bekam.

Nachfolgend eine nicht-abschliessende Liste von Vorteilen der deklarativen Security:

  • Durch die Trennung von der Geschäftslogik können sich Entwickler auf die Programmierung fachlicher Aspekte konzentrieren, die Security kann von entsprechenden Experten losgelöst deklarativ spezifiziert werden.
  • Durch den deklarativen Ansatz ist es möglich von der Komplexität der darunterliegenden Standards und Technologien zu abstrahieren.
  • Durch die zentrale Verwaltung von Security Policies können Konsistenz, Compliance und Interoperabilität verbessert werden.

Im Bereich von Web Services bieten alle wichtigen Hersteller von Middleware Produkten die Möglichkeit deklarative Security bei Service Providern wie auch Service Consumer einzusetzen (siehe auch vergangener Blogpost). Deklarative Security wird immer häufiger genutzt – zumindest auf Applikationsservern. Was viele nicht wissen: dies ist auch in gewöhnlichen Java Standard Edition (JSE) Applikationen einfach möglich.

Im Folgenden möchte ich am Beispiel des Oracle Web Services Manager (OWSM) zeigen, wie einfach beispielsweise Single Sign-on (SSO) in JSE Applikationen implementiert werden kann.

Beispiel: SSO mit OWSM in JSE Applikation

Ausgangslage

In diesem Beispiel gehen wir davon aus, dass eine Unternehmung basierend auf Oracle Middleware Produkten Web Services anbietet. Diese sollen u.a. auch von internen Java-basierten Desktop Applikationen verwendet werden können. Allerdings wird die Identität der Endbenutzer für den Zugriff auf die Services benötigt. Diese Identitäten werden in einer Active Directory (AD) Infrastruktur verwaltet.

Lösungsansatz

In einer solchen Situation bietet sich die Verwendung von Kerberos, genauer gesagt des WS-Security Kerberos Token Profiles an. Abbildung 1 zeigt die Interaktionen der Komponenten. Dabei kann für den Benutzer basierend auf der beim Windows Login erhaltenen Authentisierungsbestätigung (1) völlig transparent ein Ticket für den Zugriff auf die Services angefordert (2) und mit dem Request mitgeschickt werden (3). Der Einsatz von OWSM setzt die JAX-WS Technologie und deren Implementation durch Oracle voraus. Dadurch sind wir in der Lage auch auf der Clientseite Security deklarativ einzusetzen.

Abbildung 1: JSE Applikation propagiert Identitäten mittels Kerberos mit OWSM zu Webservices
Abbildung 1: JSE Applikation propagiert Identitäten mittels Kerberos mit OWSM zu Web Services

Umsetzung

Als Beispiel nehmen wir einen «Hello World»-artigen Web Service. Basierend auf der WSDL Datei können wir die Java Web Service (JWS) Klasse generieren. Dazu gibt es verschiedene Tools. Eine Möglichkeit ist der clientgen Ant Task von Oracle. Durch die Verwendung der generierten JWS Klasse, angereichert mit OWSM-spezifischer Konfiguration, können wir den Service in einer JSE Applikation nutzen.

public class OWSMHelloClient {
        private void invoke() {
                HelloWS client = getClient();
                String greeting = client.hello();
                System.out.println(greeting);
        }
        private HelloWS getClient() {
                HelloWSService service = new HelloWSService();
                String[] policies = getPolicies();
                HelloWS port = service.getHelloWSPort(new SecurityPoliciesFeature(policies));
                ((BindingProvider) port).getRequestContext().putAll(getStubProperties());
                return port;
        }
        private String[] getPolicies() {
                return new String[] { "oracle/wss11_kerberos_token_client_policy" };
        }
        private Map<String, Object> getStubProperties() {
                HashMap<String, Object> properties = new HashMap<String, Object>();
                properties.put("javax.xml.ws.service.endpoint.address", "http://fq.host.name:7001/HelloWS/HelloWSService");
                properties.put("oracle.wsm.service.principal.name", "http/fq.service.principal.name@MY.KRB.REALM");
                return properties;
        }
        public static void main(String[] args) {
                new OWSMHelloClient().invoke();
        }
}

Nachgehend wird die Beispiel Klasse OWSMHelloClient Schritt für Schritt erklärt. Der interessante Teil ist, wie der Client instanziiert resp. konfiguriert wird. In unserem Beispiel also die getClient() Methode. Gemäss JAX-WS wird zunächst ein Service Objekt instanziiert – in unserem Fall der HelloWSService. Diese Instanz wird benutzt um ein Web Service Stub Objekt zu beziehen. Mit OWSM verwendet man die von javax.xml.ws.Web ServiceFeature abgeleitete Klassen SecurityPolicyFeature oder SecurityPoliciesFeature um eine resp. mehrere Policies zu spezifizieren. In unserem Beispiel verwenden wir nur eine einfache Kerberos Client Policy (getPolicies Methode).

Typischerweise müssen zu den Policies noch zusätzliche Informationen via Properties des Requestkontexts mitgegeben werden. In unserem Beispiel haben wir in der getStubProperties() Methode so den Web Service Endpunkt und den Kerberos Service Principal Name (SPN) spezifiziert. Die korrekte Struktur ist hier entscheidend: da wir das HTTP Protokoll einsetzen, müssen wir den Präfix http/ verwenden. Danach kommt der voll qualifizierte Service Principal Name, typischerweise entspricht das dem Servernamen. Zuletzt kommt der Kerberos Realm Name in Grossbuchstaben. In der Microsoft Welt ist das der AD Domänenname.

Das ist bereits alles, was es an Code braucht!

Für das korrekte Funktionieren der Java Applikation braucht es noch einige Konfigurationen des Java Prozesses. Damit der OWSM Agent die Gelegenheit bekommt eine Policy durchzusetzen, müssen wir die JAX-WS Implementation von Oracle verwenden. Dazu muss lediglich die Datei oracle.webservices.standalone.client.jar im Klassenpfad referenziert sein. OWSM selbst nutzt die Oracle Platform Security Services (OPSS). Deshalb müssen wir eine entsprechende Konfiguration verwenden. Eine Beispieldatei für JSE Applikationen (jps-config-jse.xml) befindet sich jeweils im Fusion Middleware Konfigurationsverzeichnis (%DOMAIN_HOME%\fmwconfig) einer WLS Domäne. Die Datei kann mittels oracle.security.jps.config System Property beim Starten der Java Applikation übergeben werden.

Für Kerberos müssen mindestens der Realm und das Key Distribution Center (KDC) konfiguriert sein. Im einfachsten Fall kann dies mit dem Setzen der System Properties java.security.krb5.realm und java.security.krb5.kdc erreicht werden. Sind weitere Konfigurationen notwendig (bspw. Ciphersuiten), muss eine Konfigurationsdatei erstellt und mit der System Property java.security.krb5.conf referenziert werden; weitere Details findet man hier.

Das ist alles, was es für deklarative Security mit OWSM in einer JSE Applikation braucht: einige Konfigurationen und ein kleines Bisschen «Glue-Code».

Was uns der OWSM Agent abnimmt

Ein mit den Details von WS-Security wenig vertrauter Leser würde sich nun fragen, was es mit der deklarativen Security auf sich hat – immerhin sieht man im Beispiel ja v.a. Code. Dazu muss man verstehen, was der OWSM Agent aufgrund der referenzierten Policy oracle/wss11_kerberos_token_client_policy tut:

  • Auslesen des Session Keys für die Kommunikation mit dem Ticket Granting Service (TGS)
  • Beim TGS Anfordern eines Tickets für die Benutzung des Service (spezifiziert durch den konfigurierten SPN)
  • Generieren eines Security Header Elements
  • Generieren eines BinarySecurityToken (BST) Elements mit entsprechenden Attributen
  • Einbetten des Base64 codierten Tickets in das BST Element
  • Einbetten des Security Headers in die existierende Message
  • Verschicken der Nachricht
  • Verifikation des Security Headers in der erhaltenen Antwort
  • Der resultierende SOAP Header des Requests sieht dann folgendermassen aus:
<S:Header>
        <wsse:Security S:mustUnderstand="1"
                xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                <wsse:BinarySecurityToken
 EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
 ValueType="http://docs.oasis-open.org/wss/oasis-wss-kerberos-token-profile-1.1#GSS_Kerberosv5_AP_REQ"
 wsu:Id="KT-705Ao1jwzDOatgS3wQVlCA22"
 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
 YIIj1wYJKo…
                </wsse:BinarySecurityToken>
        </wsse:Security>
</S:Header>

Das alles auszuprogrammieren würde einige Zeilen Code erfordern. Weiter bietet das WS-Security Kerberos Token Profile verschiedenste Ausprägungen:

  • WS-Security in der Version 1.0 anstatt 1.1
  • Zeitstempel im Header
  • Token signiert
  • Token verschlüsselt
  • Verwendete Algorithmen und Schlüsselstärken
  • Signierte Header oder Teile der Message
  • Verschlüsselte Header oder Teile der Message

Das würde bedeuten, dass man sich auf genau eine Policy-Variante festlegt, und diese dann hartcodiert, was bei jeder Änderung zu Codeanpassungen und Redeployments führen würde. Oder aber man lässt Flexibilität via Konfiguration zu, was Codeanpassungen vermeiden würde, müsste dann aber erheblich mehr Aufwand für die Umsetzung der ganzen Details der involvierten Standards wie WS-SecurityXML-Signature und XML-Encryption betreiben. Eine Unterstützung von weiteren Tokentypen würde das Unterfangen selbstverständlich noch weiter erschweren. Dies zeigt, wie viel einfacher es ist, dem System nur sagen zu müssen, was es tun soll, und nicht wie.

Fazit

Obwohl sich deklarative Security im Aufwind befindet, ist vielen Leuten nicht bekannt, dass diese auch in JSE Applikationen eingesetzt werden kann. Anhand eines einfachen Beispiels konnte gezeigt werden, was es im Fall von OWSM braucht: einige Konfigurationen und ein klein wenig «Glue-Code». Diese Variante ist sehr generisch und deswegen für viele Anwendungsfälle einsetzbar. Nebst Kerberos aus dem Beispiel sind damit auch die anderen üblichen Verdächtigen im Bereich WS-Security – nämlich Username Token, SAML Token und X509 Token – nur mit Anpassungen in der Konfiguration abgedeckt.

Dieser Ansatz hat den Vorteil, dass auch im Bereich der Desktop Applikationen Security konfiguriert werden kann ohne eine Codezeile zu programmieren. Tausendfach getestete Funktionalität wird wiederverwendet, welche zudem die zugrundeliegenden Implementation der komplexen Security Standards versteckt. Hierdurch können potentielle Programmierfehler reduziert, die Effizienz in der Umsetzung gesteigert, und gleichzeitig die Sicherheit und Flexibilität im Betrieb erhöht werden. Neben all den genannten Vorteilen der deklarativen Security, kann oben drauf alles in allem eine Menge Geld gespart werden.


Lesen Sie auch

famous for integration