Nutzungsvorgaben Security

Java Bibliothek / IT-System

Name Art Version

isy-security

Bibliothek

v3.0.2

1. Einleitung

Dieses Dokument beschreibt, wie der Baustein Security und die von ihm bereitgestellte Bibliothek isy-security verwendet werden, um die Autorisierung innerhalb von IT-Systemen umzusetzen. Es richtet sich an Entwickler, die ein IT-System gemäß den Vorgaben der IsyFact mit der Fähigkeit zur Autorisierung ausstatten müssen und beschreibt, wie und in welchen Teilen einer Anwendung die Autorisierung umzusetzen ist. Der Baustein Security ist OAuth2.0 konform und greift hauptsächlich auf die Möglichkeiten von Spring Security zurück.

Die folgenden Kapitel leiten durch die Funktionen des Bausteins isy-security.

Schließlich beschreibt das letzte Kapitel Testunterstützung, welche Hilfen der Baustein isy-security zum Erstellen von (automatisierten) Tests bereitstellt.

Die konzeptionellen Aspekte der Authentifizierung und Autorisierung werden im Security - Konzept beschrieben.

2. Software-Architektur des Security Bausteins

Die Software-Architektur des Security-Bausteins bildet das Fundament für die Sicherheit von Anwendungen in modernen Softwarelösungen. Die korrekte Gestaltung und Implementierung dieser Architektur ist entscheidend, um Schutzmechanismen gegen potenzielle Sicherheitsbedrohungen zu etablieren. In diesem Kapitel wird detailliert auf die verschiedenen Aspekte der Software-Architektur des Security-Bausteins eingegangen und bewährte Methoden sowie bewährte Praktiken vorgestellt, um eine robuste und zuverlässige Sicherheitsinfrastruktur zu schaffen.

Im Folgenden werden die Interfaces des Bausteins Security genauer erläutert.

2.1. Security

Das Interface Security stellt die zentrale Schnittstelle bereit, über die auf den Baustein zugegriffen wird.

Es empfiehlt sich, die Beans direkt zu 'autowiren' und das Interface hauptsächlich für eine Migration vom alten Sicherheitsbaustein isy-sicherheit auf den neuen Baustein isy-security zu verwenden.

Die folgenden Methoden bieten Zugriff auf eine Liste von allen im System hinterlegten Rollen, den Berechtigungsmanager und den optionalen Authentifizierungsmanager:

Set<String> getAlleRollen()

Liefert die Menge aller im System konfigurierten Rollen.

Berechtigungsmanager getBerechtigungsManager()

Stellt eine Referenz auf das Berechtigungsmanager Bean bereit.

Optional<Authentifizierungsmanager> getAuthentifizierungsManager()

Liefert ein Optional, das eine Referenz auf den Authentifizierungsmanager enthält, oder null falls die Anwendung als Resource-Server agiert und kein Authentifizierungsmanager benötigt wird.

2.2. Authentifizierungsmanager

Zur Authentifizierung von OAuth 2.0 Clients stehen folgende Methoden zur Verfügung:

void authentifiziere(String oauth2ClientRegistrationId)

Authentifiziert einen OAuth 2.0 Client anhand seiner Client-Registration-ID. Unterstützt den client_credentials (Client Credentials Flow) und password (Resource Owner Password Credentials Flow) GrantType. Zusätzliche Authentifizierungsmerkmale werden aus den IsyFact spezifischen Konfigurationsparametern mit der entsprechenden Client-Registration-ID abgefragt:

  • bhknz: Das BHKNZ kann als optionaler Parameter übergeben werden. Wird aktuell nur für den Resource Owner-Password Credentials Flow ausgewertet.

  • username und password: Die Zugangsdaten des technischen Nutzers (Resource Owner), welche für den Resource Owner Password Credentials Flow erforderlich sind.

void authentifiziereClient(String issuerLocation, String clientId, String clientSecret)
void authentifiziereClient(String issuerLocation, String clientId, String clientSecret, String bhknz)

Authentifiziert einen OAuth 2.0 Client gegen die angegebene issuerLocation anhand seiner clientId und clientSecret. Das bhknz kann als optionaler Parameter übergeben werden, wird aber für die Client-Authentifizierung noch nicht ausgewertet.

void authentifiziereSystem(String issuerLocation, String clientId, String clientSecret, String username, String password)
void authentifiziereSystem(String issuerLocation, String clientId, String clientSecret, String username, String password, String bhknz)

Authentifiziert einen technischen Nutzer (Resource Owner) gegen die angegebene issuerLocation anhand der clientId und clientSecret des zu verwendenden OAuth 2.0 Clients und dem username und password des Resource Owners. Das bhknz kann als optionaler Parameter übergeben werden und wird in den konfigurierten Header zur Zwei-Faktor-Authentifizierung gesetzt.

2.3. Berechtigungsmanager

Berechtigungsprüfungen können ebenso an beliebiger Stelle im Quellcode erfolgen. Dazu stellt der Baustein isy-security über die Schnittstelle Berechtigungsmanager entsprechende Funktionen bereit. Der Berechtigungsmanager ist ein Container für die Rechte und Rollen des aktuell authentifizierten Anwenders.

Zur Formulierung von Berechtigungsprüfungen stehen folgende Methoden des Berechtigungsmanagers zur Verfügung:

Set<String> getRollen()

Liefert die Menge aller Rollen des Anwenders.

Set<String> getRechte()

Liefert die Menge aller Rechte des Anwenders.

boolean hatRecht(String recht)

Ermittelt, ob der Anwender ein bestimmtes Recht besitzt.

void pruefeRecht(String recht)

Prüft, ob der Anwender das angegebene Recht besitzt und erzeugt einen Fehler vom Typ AccessDeniedException, wenn das nicht der Fall ist.

Object getTokenAttribute(String key)

Fragt einen Wert im Access Token des Security Contexts ab.

Primär sollte die Berechtigungsprüfung über die Spring Annotation @Secured und nicht über die hier erwähnte Methode pruefeRecht(String recht) erfolgen.

3. Aufrufen von Nachbarsystemen

Der Aufruf von Nachbarsystemen erfolgt auf Basis des reaktiven Spring WebClient. Um Nachbarsysteme aufzurufen, die auf Basis von OAuth 2.0 abgesichert sind, ist grundlegend die Authentifizierung bei einem IAM-Service mit gültiger client_id, client_secret sowie grant_type erforderlich. Dieser liefert ein Bearer-Token zurück, in dem die Rollen und somit die Zugriffsmöglichkeiten der aufrufenden Anwendung definiert sind.

3.1. Maven Dependencies

Für die Authentifizierung mit einem OAuth 2.0-Client sind die folgenden Abhängigkeiten erforderlich:

Listing 1. Maven Dependencies für Client Credentials
<dependency>
  <groupId>de.bund.bva.isyfact</groupId>
  <artifactId>isy-security</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

3.2. Konfigurationsparameter

Konfigurationsparameter sind essenziell, um Bausteine in technischen Anwendungen anzupassen und zu steuern. Sie bieten die Möglichkeit, die Funktionalität und das Verhalten eines Bausteins entsprechend spezifischer Anforderungen anzupassen. In diesem Kapitel werden alle möglichen Optionen der Konfigurationsparameter vorgestellt, um einen umfassenden Überblick über deren Anwendungsmöglichkeiten zu geben.

3.2.1. Spring OAuth2 Client Konfigurationsparameter

Für die korrekte Anbindung an den IAM-Service ist die Angabe einiger Konfigurationsparameter notwendig, welche in der Konfigurationsdatei der Anwendung eingetragen werden müssen. Das Präfix sämtlicher Konfigurationsparameter spring.security.oauth2 wird zur Vereinfachung in der folgenden Liste weggelassen.

Tabelle 1. Konfigurationsparameter einer Client-Anwendung
Parameter Wertebereich Beschreibung

client.registration.<registrationId>.client-id

String

Identifier der Client-Applikation im Autorisierungsserver.

client.registration.<registrationId>.client-secret

String

Secret, das nur der Anwendung und dem Autorisierungsserver bekannt ist.

client.registration.<registrationId>.authorization-grant-type

String

Methode, durch welche die Anwendung ein Bearer-Token erhält. Abhängig von dem zu verwendenden Flow auf client_credentials (Client Credentials Flow) oder password (Resource Owner Password Credentials Flow) zu setzen.

client.registration.<registrationId>.provider

String

(Optional) ID des zu verwendenden Client-Providers, falls diese nicht mit der Registration-ID übereinstimmt.

client.provider.<providerId>.issuer-uri

String

Der OpenID Connect Issuer Identifier für die Konfiguration des Clients über den Discovery Endpoint, beispielsweise in der Form http://<identity-provider>/auth/realms/<realm>;.

3.2.2. Isyfact spezifische Konfigurationsparameter

Deprecation

Dieser Teil der Dokumentation ist veraltet und wird in einem zukünftigen Release entfernt. Die Inhalte sollten zur Entwicklung neuer Anwendungen nicht mehr berücksichtigt werden. Stattdessen wird empfohlen, [client-credential-flow] zu verwenden.

Anmerkung zur Deprecation

Dieser Flow soll gemäß OAuth2.0 Vorgaben nicht mehr verwendet werden und wird in zukünftigen Versionen aus dem OAuth-Standard entfernt.

Die folgenden Konfigurationsparameter werden als Übergangslösung für technische Nutzer bereitgestellt. Der Client-Credentials Flow ist bevorzugt zu nutzen.

Zugangsdaten für technische Nutzer zur Authentifizierung von Tasks und Batches mit dem Resource-Owner-Password-Credential Flow können über die folgenden Parameter konfiguriert werden:

Tabelle 2. Isyfact spezifische Erweiterung der Spring ClientRegistration
Parameter Wertebereich Default Beschreibung

isy.security.oauth2.client.registration.<registrationId>.username

String

null

Benutzername für Grant-Type password.

isy.security.oauth2.client.registration.<registrationId>.password

String

null

Benutzerpassword für Grant-Type password.

isy.security.oauth2.client.registration.<registrationId>.bhknz

String

null

Behördenkennzeichen für Grant-Type password. (optional)

isy.security.oauth2.client.bhknz-header-name

String

x-client-cert-bhknz

Konfigurierbarer Headername für Zwei-Faktor-Authentifizierung.

isy.security.oauth2.client.default-certificate-ou

String

null

Organisational Unit, die in Verbindung mit bhknz verwendet wird, um den Wert für bhknz-header-name zu bilden.

(Muss gesetzt werden, wenn eine registration mit bhknz konfiguriert ist)

3.3. Tokenweitergabe an Nachbarsysteme

Für authentifizierte Anwendungen besteht die Möglichkeit der Tokenweitergabe an Nachbarsysteme durch Erweiterung des WebClient um die ServletBearerExchangeFilterFunction. Diese liest die aktuelle Authentifizierung aus dem SecurityContext und fügt ausgehenden Requests das dort befindliche OAuth 2.0 Token dem Authorization Header hinzu.

Listing 2. Konfiguration des Spring WebClient zur Tokenweitergabe
@Configuration
public class ExampleWebClientConfiguration {

  @Bean
  WebClient webClient() {
      ServletBearerExchangeFilterFunction bearer = new ServletBearerExchangeFilterFunction();
      return WebClient.builder()
              .filter(bearer).build();
  }

}

3.4. Tokenweitergabe bei generierten Clients

Der von Open API Tools generierte ApiClient erlaubt die Initialisierung mit einem Spring WebClient im Konstruktor. Demzufolge kann die oben beschriebene Konfiguration zur Tokenweitergabe analog angewendet werden. Für das folgende Code-Beispiel wird somit unterstellt, dass ein WebClient vorliegt, der die ServletBearerExchangeFunction beinhaltet:

Listing 3. Initialisierung des ApiClient mit WebClient im Konstruktor
public class ExampleClass {
    // ...
    ApiClient apiClient = new ApiClient(webClient);
}

3.5. Mögliche Fehler bei der Tokenweitergabe

Bei der Tokenweitergabe können verschiedene Fehler auftreten. Im Folgenden werden diesbezüglich die naheliegendsten Fehler beschrieben.

3.5.1. Ungültigkeit des Tokens

Wenn das Token im SecurityContext ungültig ist, wird eine InvalidBearerTokenException geworfen und der Fehlercode 401: Unauthorized zurückgegeben. Die Ungültigkeit kann auf verschiedenste Gründe zurückzuführen sein, beispielsweise könnte das Token abgelaufen oder fehlerhaft sein. Eine detaillierte Beschreibung des jeweiligen Fehlers ist unter der Error-URI zu finden:

Listing 4. Beispielhafte Response bei ungültigem Token
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error_code="invalid_token", error_description="Unsupported algorithm of none", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"

3.5.2. Kein Token im Security Context hinterlegt

Wenn im SecurityContext kein Token vorliegt, beispielsweise weil die Anwendung nicht authentifiziert ist, hat dies zur Folge, dass der ausgehende Request nicht um Authentifizierungsinformationen im Header ergänzt wird. Der aufgerufene Server liefert in diesem Fall außer dem Fehlercode 401: Unauthorized keine tiefergehenden Fehlerinformationen zurück.

Listing 5. Beispielhafte Response ohne Token
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example"

3.6. Authentifizierung eines OAuth 2.0 Clients mit dem Authentifizierungsmanager

Ein OAuth 2.0 Client kann über den Authentifizierungsmanager authentifiziert werden, welcher entsprechende Methoden zur Verwendung mit und ohne zuvor konfigurierte ClientRegistrations bereitstellt.

  1. Authentifizierung per oauth2ClientRegistrationId. Die Auswahl des Flows (Client Credentials oder Resource Owner Password Credentials) wird dabei über den authorization-grant-type der vollständig konfigurierten ClientRegistration getroffen (siehe Beispiel: Spring Security OAuth 2.0 Client-Registrations). Diese Methode eignet sich in Anwendungen, wenn die Zugangsdaten für technische Nutzer zur Konfiguration bekannt sind.

  2. Authentifizierung per issuerLocation, clientId und clientSecret. Diese Methode nutzt den Client Credentials Flow und kann verwendet werden, wenn die Zugangsdaten des OAuth 2.0 Clients (clientId und clientSecret) erst zur Laufzeit bekannt sind. Die issuerLocation verweist auf den OpenID Connect Provider, in dem der für den Client Credentials Flow konfigurierte OAuth 2.0 Client hinterlegt ist und als IAM-Service die Authentifizierung mit den übergebenen Authentifizierungsdaten durchgeführt wird.

  3. Authentifizierung per issuerLocation, clientId, clientSecret, username und password. Diese Methode nutzt den Resource Owner Password Credentials Flow und kann verwendet werden, wenn die Zugangsdaten des technischen Nutzers (username und password des Resource Owners) erst zur Laufzeit bekannt sind. Die Zugangsdaten des Vermittlers (clientId und clientSecret) werden benötigt, um die Authentifizierungsabfrage selbst zu autorisieren. Die issuerLocation verweist auf den OpenID Connect Provider, gegen den die Authentifizierung mit dem dort für den Resource Owner Password Credentials Flow konfigurierten OAuth 2.0 Client durchgeführt werden soll. Die im Beispiel: Spring Security OAuth 2.0 Client-Registrations konfigurierten IsyFact-spezifischen ClientRegistration-Properties werden nicht benötigt, da die Zugangsdaten der Methode direkt übergeben werden.

Listing 6. Beispiel: Spring Security OAuth 2.0 Client-Registrations
[...]
spring.security.oauth2.client.provider.testrealm.issuer-uri=http://localhost:9095/auth/realms/testrealm

### Beispiel: ClientRegistration für den Client Credentials Flow
# Spring Security Konfiguration
spring.security.oauth2.client.registration.client1.client-id=client-credentials-client
spring.security.oauth2.client.registration.client1.client-secret=client-credentials-secret
spring.security.oauth2.client.registration.client1.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.client1.provider=testrealm

### Beispiel: ClientRegistration für den Resource Owner Password Credentials Flow
# Spring Security Konfiguration
spring.security.oauth2.client.registration.client2.client-id=client-credentials-client
spring.security.oauth2.client.registration.client2.client-secret=client-credentials-secret
spring.security.oauth2.client.registration.client2.authorization-grant-type=password
spring.security.oauth2.client.registration.client2.provider=testrealm
# Isyfact spezifische ClientRegistration properties für den Resource Owner Password Credentials Flow
isy.security.oauth2.client.registration.client2.username=testuser
isy.security.oauth2.client.registration.client2.password=testpassword
isy.security.oauth2.client.registration.client2.bhknz=123456
# Organizational Unit zur Verwendung mit bhknz
isy.security.oauth2.client.default-certificate-ou=TESTOU

3.7. Automatische Authentifizierung eines WebClient mit Client Credentials

Mit dem OAuth 2.0-Grant client_credentials steht eine vereinfachte Zugriffskontrolle für eine Client-Anwendung im Rahmen des Client Credentials Flow zur Verfügung.

Spring Security bietet umfassenden Support für OAuth 2.0 und unterstützt die Authentifizierung interner Systeme auf Basis des Client Credentials Flow, wobei auf Spring interne Mechanismen zugegriffen wird.

Um clientseitig die automatische Konfiguration einer Anwendung zu ermöglichen, werden zunächst die oben aufgeführten Abhängigkeiten eingebunden und eine Client-Registration für den Client Credentials Flow angelegt. Um im Rahmen des Client Credentials Flows HTTP-Anfragen an einen Ressourcenserver stellen zu können, ist zusätzlich die Konfiguration des reaktiven WebClient erforderlich. Hierzu wird die Standardimplementierung mit dem Zusatz eines OAuth2-Autorisierungsfilters verwendet. Die "registrationId" ist hierbei auf die ID der für den Client Credentials Flow konfigurierten Client-Registration zu setzen.

Listing 7. Spring WebClient Beispiel
@Configuration
public class OAuth2WebClientConfiguration {

    @Bean("webclient")
    WebClient webClient(@Qualifier(ISY_AUTHORIZED_CLIENT_MANAGER_BEAN) OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth.setDefaultClientRegistrationId("registrationId");
        return WebClient.builder().filter(oauth).build();
    }

}

4. Authentifizierung über das SGW am IAM-Service

Die Authentifizierung über das Service Gateway (SGW) stellt einen wichtigen Schritt in der Sicherung von Anwendungen dar. Um diesen Prozess effektiv umzusetzen, empfiehlt sich die Verwendung des neu entwickelten Authentifizierungsmanagers und dessen Methoden.

Die Methoden erlauben es technische Nutzer zur Laufzeit mit ihren Zugangsdaten über den IAM-Service zu authentifizieren. Mit diesem Mechanismus wird sichergestellt, dass nur autorisierte Clients Zugriff auf die Ressourcen erhalten. Durch die Nutzung dieses speziellen Authentifizierungsmechanismus wird eine robuste und zuverlässige Sicherheitsschicht implementiert, um unautorisierten Zugriff und potenzielle Sicherheitslücken zu verhindern.

Weitere Informationen und detaillierte Anleitungen zur Verwendung können im Abschnitt zum Authentifizierungsmanager gefunden werden.

5. Absicherung von Batches und Tasks

Für detaillierte Informationen und Anleitung zur Absicherung von Batches und Tasks wird auf die Dokumentation unter

verwiesen. Diese bieten praxisorientierte Richtlinien zur Gewährleistung der Sicherheit von Batches und Tasks.

6. Absicherung von Service-Schnittstellen

Spring Security unterstützt die automatische Absicherung von Endpunkten einer Anwendung und eine feingranulare Autorisierung im Rahmen von OAuth 2.0.

6.1. Maven Dependency

Dazu ist die Einbindung der folgenden Abhängigkeiten notwendig:

Listing 8. Maven Dependencies für Client Credentials
<dependency>
  <groupId>de.bund.bva.isyfact</groupId>
  <artifactId>isy-security</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

6.2. Spring Konfiguration

Um eine Anwendung auf Basis von OAuth 2.0 und unter Verwendung von JWT-Bearer-Tokens zu sichern, werden die oben aufgeführten Abhängigkeiten eingebunden. Die Verwendung der Abhängigkeit spring-security-oauth2-resource-server führt dazu, dass die anwendungsspezifische SecurityFilterChain um den Filter .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) erweitert wird. Bietet die Applikation keine SecurityFilterChain an, wird durch Spring Boot automatisch die folgende Konfiguration eingebunden:

Listing 9. Beispiel für eine Security Konfiguration zur Absicherung einer Schnittstelle mit Spring Boot
@Configuration
@EnableWebSecurity
public class OAuth2ServerSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }

}

6.3. Konfigurationsparameter

Für die korrekte Anbindung an den IAM-Service ist die Angabe der folgenden Konfigurationsparameter notwendig. Das Präfix sämtlicher Konfigurationsparameter spring.security.oauth2 wird zur Vereinfachung in der Liste unten weggelassen.

Tabelle 3. Konfigurationsparameter einer Service-Schnittstelle
Parameter Wertebereich Default Beschreibung

resourceserver.jwt.issuer-uri

String

keiner

Auto-Konfiguration des IAM-Services zur Validierung des Bearer Tokens, beispielsweise in der Form "http://<identity-provider>/auth/realms/<realm>/protocol/openid-connect/certs"

Zusätzlich sind folgende Isyfact-spezifischen Konfigurationsparameter notwendig.

Tabelle 4. Isyfact-spezifische Konfigurationsparameter einer Service-Schnittstelle
Parameter Wertebereich Default Beschreibung

isy.security.rolesClaimName

String

roles

Name des Claims im JWT Token der die Rollen enthält

6.4. Konfiguration von Rollen und Rechten

Jede Geschäftsanwendung spezifiziert im Rahmen ihrer Systemspezifikation Rechte und bildet diese auf fachliche und technische Rollen ab. Bei der technischen Umsetzung müssen alle spezifizierten Rollen und Rechte konfiguriert und korrekt zugeordnet werden.

Der Pfad der Datei kann wie folgt angepasst werden:

Tabelle 5. Konfiguration von Rollen und Rechten
Parameter Wertebereich Default Beschreibung

isy.security.role-privilege-mapping-file

Resource

classpath:/resources/sicherheit/rollenrechte.xml

Pfad zu der XML-Datei, welche die Rollen-/Berechtigungszuordnungen enthält.

Die Bereitstellung einer Datei mit den erforderlichen Rollen und Rechten erfolgt optional. Im Falle einer nicht vorhandenen Datei verbleibt die Liste der Rollen und Rechte leer, d. h. eine Absicherung der Anwendung findet nicht statt. Die Verantwortung zur korrekten Bereitstellung der erforderlichen Rollen und Rechten liegt bei der Anwendung.

Dieser Zusammenhang wird mit einem Beispiel verdeutlicht: Die Geschäftsanwendung X spezifiziert zwei Rechte, DialogA.Aufrufen und DialogB.Aufrufen. Aus diesen werden zwei fachliche Rollen gebildet:

  • FAX_DialogNutzerA darf nur Dialog A aufrufen,

  • FAX_DialogNutzerAlle darf Dialog A und Dialog B aufrufen.

Dieses Beispiel führt zu folgender Konfigurationsdatei:

Listing 10. Beispielhafte Definition von Rollen und Rechten
<tns:Anwendung AnwendungsId="GeschäftsanwendungX"
    xmlns:tns="http://www.isyfact.de/RollenRechteSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.isyfact.de/RollenRechteSchema RollenRechteSchema.xsd ">

    <!-- Definition der Rechte -->
    <tns:rechte>
        <tns:rechtId Id="DialogA.Aufrufen" />
    </tns:rechte>

    <tns:rechte>
        <tns:rechtId Id="DialogB.Aufrufen" />
    </tns:rechte>

    <!-- Definition der Rollen -->
    <tns:rollen RolleId="FAX_DialogNutzerA">
        <tns:rechtId Id="DialogA.Aufrufen" />
    </tns:rollen>

    <tns:rollen RolleId="FAX_DialogNutzerAlle">
        <tns:rechtId Id="DialogA.Aufrufen" />
        <tns:rechtId Id="DialogB.Aufrufen"/>
    </tns:rollen>
</tns:Anwendung>

6.5. Zusammenhang von Rechten und Rollen

Innerhalb jeder Rolle werden gemäß Spezifikation die zugeordneten Rechte festgelegt. Rollen können überlappende Teilmengen von Rechten enthalten.

Die Konfiguration muss die folgenden Anforderungen erfüllen:

  • Es sind alle in der Geschäftsanwendung spezifizierten Rechte definiert.

  • Es sind alle in der Geschäftsanwendung spezifizierten Rollen definiert.

Werden in Überprüfungen Rollen oder Rechte verwendet, die hier nicht definiert sind, wird ein technischer Fehler erzeugt. Die Konfiguration gibt also verlässlich Auskunft darüber, welche Rollen und Rechte in der Geschäftsanwendung überprüft werden.

Der Baustein Security ermöglicht eine Autorisierung nur auf Basis von Rechten, nicht von Rollen. Jeder Rolle muss also zumindest ein Recht zugeordnet werden, um anhand dessen eine Autorisierung durchführen zu können. Werden im Lebenszyklus der Geschäftsanwendung weitere Rollen (z.B. für neu hinzukommende Akteure) spezifiziert und mit bestehenden Rechten ausgestattet, sind neben den Änderungen in der Konfigurationsdatei keine weiteren Änderungen notwendig.

6.6. Autorisierung an einer Service-Schnittstelle

Zentral für die Autorisierung ist die von Spring bereitgestellte Annotation @Secured, die alle benötigten Rechte des Aufrufers überprüft. Eine Autorisierung direkt über die Rollen des Aufrufers ist nicht möglich.

Die Annotation kommt in der Service-Schicht zur Anwendung und autorisiert den Zugriff auf eine Service-Methode. Sollten alle Methoden die gleiche Autorisierung erfordern, kann alternativ die Annotation auch an der Service-Klasse verwendet werden.

Folgendes Beispiel (Absichern einer Service-Methode) verdeutlicht die Implementierung einer abgesicherten Service-Methode.

Listing 11. Absichern einer Service-Methode
@Secured({ "PRIV_RechtA", "PRIV_RechtB" })
public void abgesicherteMethode(...) {
    ...
}

Als Parameter wird ein Array von Rechten übergeben. Die Rechte sind disjunktiv verknüpft. Die Autorisierung erfolgt dementsprechend wenn der Nutzer mindestens eins der in der Secured Annotation übergebenen Rechte besitzt.

7. Attribute aus dem Bearer Token abfragen

Bei der Befüllung des Spring Security Context wird das zur Befüllung genutzte Bearer Token ebenfalls im Context abgelegt. Dadurch ist der Zugriff auf Attribute des Bearer Tokens über den Spring Security Context möglich. Der Zugriff kann über den Berechtigungsmanager oder direkt über den Spring Security Context erfolgen.

Für Zugriff über den Berechtigungsmanager kann die Methode getTokenAttribute(String key) genutzt werden. Das Beispiel Zugriff auf das Behördenkennzeichen veranschaulicht den Zugriff auf das im Attribut bhknz gespeicherte Behördenkennzeichen.

Listing 12. Zugriff auf das Behördenkennzeichen
String bhknz = (String) this.berechtigungsmanager.getTokenAttribute(BEARER_TOKEN_ATTR_BHKNZ);

Für den direkten Zugriff per Security Context kann die Authentication abgefragt und per Cast als ein AbstractOAuth2TokenAuthenticationToken zugegriffen werden. Detaillierte Informationen hierzu können der Spring Dokumentation entnommen werden.

8. Automatische Authentifizierung innerhalb von Methoden

Wenn Client Registrations für OAuth 2.0 Clients in der Anwendung angelegt wurden (vgl. Konfigurationsparameter), kann die @Authenticate-Annotation verwendet werden, um anhand dieses Clients automatisch Aufrufe innerhalb der mit der Annotation versehenen Methode zu authentifizieren. Der dabei verwendete OAuth 2.0 Flow wird anhand der Konfiguration der Client Registration ausgewählt.

Dafür muss in der Annotation die Client Registration ID des zu verwendenden OAuth 2.0 Clients angegeben werden. Dies kann entweder durch Hardcoden der ID oder über die dynamische Auflösung eines Property Placeholders erfolgen. Die Client Registration ID kann über verschiedene Attribute der Annotation (kein Attribut, value-Attribut, oauth2ClientRegistrationId-Attribut) angegeben werden, welche von der Funktionalität identisch sind. Die einzige Voraussetzung ist, dass die Client Registration ID in einem dieser Attribute angegeben wurde.

Listing 13. Verwendung der Annotation
@Authenticate("my-auth-client") (1)
void methodThatNeedsAuthentication() {
    ...
}

@Authenticate("${test.auth.client-id}") (2)
void methodThatNeedsAuthentication() {
    ...
}

@Authenticate(oauth2ClientRegistrationId = "my-auth-client") (3)
void methodThatNeedsAuthentication() {
    ...
}
1 Angabe der Client Registration ID ohne Verwendung von Attributnamen
2 Auflösung der Client Registration ID aus dem Wert der Property test.auth.client-id
3 Angabe der Client Registration ID mit dem Attributnamen oauth2ClientRegistrationId

9. Testunterstützung

In diesem Abschnitt wird isy-security-test beschrieben, das zum Erstellen von (automatisierten) Tests einen OpenId Connect Provider Mock bereitstellt. Der OpenId Connect Provider Mock wurde mithilfe von WireMock realisiert. Es werden die folgenden OAuth2 und OpenID Connect Verfahren unterstützt.

  • Client Credentials Flow

  • Resource Owner Password Credentials Flow

Das folgende Beispiel zeigt die Initialisierung des EmbeddedOidcProviderMock mit einem Client mit genau einer Rolle und mit einem User mit derselben Rolle:

Listing 14. Beispiel für die Erstellung des EmbeddedOidcProviderMock
EmbeddedOidcProviderMock embeddedOidcProvider = new EmbeddedOidcProviderMock("localhost", 9095, "/auth/realms/testrealm");

embeddedOidcProvider.addClient("client-credentials-client", "supersecretpassword", Collections.singleton("Test_Role"));
embeddedOidcProvider.addUser("ressource-owner-password-credentials-client", "hypersecretpassword", "admin", "adminpassword", Optional.of("123456"), Collections.singleton("Test_Role"));

Detaillierte Informationen zur Verwendung des EmbeddedOidcProviderMock können dem JavaDoc der Klasse entnommen werden.

10. Anhang

10.1. Anhang A: Rollen-Rechte-Schema

Im Folgenden ist der Inhalt der Datei RollenRechteSchema.xsd wiedergegeben, die das Format der Konfigurationsdatei für Rollen und Rechte (rollenrechte.xml) festlegt.

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.isyfact.de/RollenRechteSchema"
    xmlns:tns="http://www.isyfact.de/RollenRechteSchema"
    elementFormDefault="qualified">
  <include schemaLocation=""></include>
  <complexType name="Rolle">
    <sequence>
      <element name="rechtId" type="tns:RechtId" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
    <attribute name="RolleId" type="string" use="required"/>
  </complexType>
  <complexType name="Recht">
    <sequence>
      <element name="rechtId" type="tns:RechtId" minOccurs="1" maxOccurs="1"/>
    </sequence>
  </complexType>
  <element name="Anwendung" type="tns:Anwendung"/>
  <complexType name="Anwendung">
    <sequence>
      <element name="rollen" type="tns:Rolle" minOccurs="1" maxOccurs="unbounded"/>
      <element name="rechte" type="tns:Recht" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
    <attribute name="AnwendungsId" type="string" use="required"/>
  </complexType>
  <complexType name="RechtId">
    <attribute name="Id" type="string" use="required"/>
  </complexType>
</schema>