Nutzungsvorgaben Security

IFS-Logo Diese Seite ist ein Teil der IsyFact-Standards. Alle Inhalte der Seite, insbesondere Texte und Grafiken, sind urheberrechtlich geschützt. Alle urheberrechtlichen Nutzungs- und Verwertungsrechte liegen beim Bundesverwaltungsamt.

Creative Commons Namensnennung Die Nutzung ist unter den Lizenzbedingungen der Creative Commons Namensnennung 4.0 International gestattet.

Java Bibliothek / IT-System

Name Art Version

isy-security

Bibliothek

v4.0.x

isy-security-test

Bibliothek

v4.0.x

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'.

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 authentifiziere(String oauth2ClientRegistrationId, Duration expirationTimeOffset)

Authentifiziert einen OAuth 2.0 Client anhand seiner Client-Registration-ID mittels der Methode authentifiziere(String oauth2ClientRegistrationId), falls das im SecurityContext vorhandene AbstractOAuth2TokenAuthenticationToken abgelaufen ist. Über den Parameter expirationTimeOffset wird eine Zeitspanne angegeben, während der das Token bereits vor dem eigentlichen Ablaufzeitpunkt als abgelaufen gilt. Eine Authentifizierung mittels authentifiziere(String oauth2ClientRegistrationId) wird ebenfalls dann vorgenommen, wenn keine Authentication vom Typ AbstractOAuth2TokenAuthenticationToken im SecurityContext vorliegt.

Für den client_credentials (Client Credentials Flow) GrantType erfolgt unabhängig vom Parameter expirationTimeOffset eine erneute Authentifizierung nur dann, wenn das Token weniger als 60 Sekunden vor Ablauf steht. Dieser Wert ist als Standardwert durch eine Implementierung in Spring Security vorgegeben.
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
<dependencies>
    <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>
</dependencies>

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-Credentials-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
<dependencies>
    <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>
</dependencies>

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-spezifische Konfigurationsparameter notwendig.

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

isy.security.roles-claim-name

String

roles

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

Der in isy.security.roles-claim-name konfigurierte Wert unterstützt keine Verschachtelungen. Somit kann bspw. ein Claim custom.roles (Claim custom mit untergeordnetem Claim roles) nicht aufgelöst werden. Je nach verwendetem Identity Provider muss ggf. ein zusätzliches Mapping der Rollen auf den konfigurierten Wert erfolgen.

Es ist sicherzustellen, dass das konfigurierte Roles-Claim auch vom Identity Provider befüllt werden.

6.4. Konfiguration von Multi-Tenancy

Für die Unterstützung von Multi-Tenancy ist es erforderlich, die Eigenschaft spring.security.oauth2.resourceserver.jwt.issuer-uri zu deaktivieren (auf false setzen) oder zu entfernen. Die nachstehenden Konfigurationsparameter werden zur Einstellung von Multi-Tenancy verwendet. Aus Gründen der Übersichtlichkeit wird das Präfix isy.security.oauth2 in der unten aufgeführten Tabelle weggelassen.

Tabelle 5. Konfigurationsparameter für Multi-Tenancy
Parameter Wertebereich Default Beschreibung

resourceserver.jwt.tenants.<tenant>.issuer-uri

String

keiner

URI des Ausstellers des JWT, spezifisch für den Tenant

resourceserver.jwt.tenants.<tenant>.jwk-set-uri

String

keiner

URI des JWK-Sets, das die öffentlichen Schlüssel bereitstellt, welche zur Validierung der JWT-Signatur verwendet werden

Nachfolgend ist eine Beispielkonfiguration für Multi-Tenancy dargestellt.

Listing 10. Multi-Tenancy Beispiel Konfiguration
isy.security.oauth2.resourceserver.jwt.tenants.tenant-1.issuer-uri=http(s)://<keycloak-host>:<keycloak-port>/auth/realms/<keycloak-realm-1> (1)
isy.security.oauth2.resourceserver.jwt.tenants.tenant-1.jwk-set-uri=http(s)://<keycloak-host>:<keycloak-port>/auth/realms/<keycloak-realm-1>/protocol/openid-connect/certs (2)

isy.security.oauth2.resourceserver.jwt.tenants.tenant-2.jwk-set-uri=http(s)://<keycloak-host>:<keycloak-port>/auth/realms/<keycloak-realm-2> (3)
isy.security.oauth2.resourceserver.jwt.tenants.tenant-2.jwk-set-uri=http(s)://<keycloak-host>:<keycloak-port>/auth/realms/<keycloak-realm-2>/protocol/openid-connect/certs (4)
1 URI des Ausstellers des JWT für tenant-1, z.B. die Authentifizierungs-URL von Keycloak für das Realm <keycloak-realm-1>.
2 URI des JWK-Sets für tenant-1, das die öffentlichen Schlüssel enthält, die zur Validierung der JWT-Signatur verwendet werden.
3 URI des Ausstellers des JWT für tenant-2, z.B. die Authentifizierungs-URL von Keycloak für das Realm <keycloak-realm-2>.
4 URI des JWK-Sets für tenant-2, das die öffentlichen Schlüssel enthält, die zur Validierung der JWT-Signatur verwendet werden.

6.5. Konfiguration von mehreren IAM-URIs

Zur Konfiguration von mehreren IAM-URIs ist es erforderlich, die Konfigurationsproperties issuer-uri und jwk-set-uri entsprechend zu definieren.

Der bisherige Property-Pfad

  • spring.security.oauth2.resourceserver.jwt ist durch

  • isy.security.oauth2.resourceserver.jwt.tenants.<tenant-id> zu ersetzen,

wobei die tenant-id einzig als Gruppierungskriterium für die beiden Properties issuer-uri und jwk-set-uri eingesetzt wird. Somit kann diese beispielsweise mit fachlich aussagekräftigen Namen, wie der Name der Geschäftsanwendung, des Kommunikationskanals oder der Netzwerkzone, versehen werden.

Die nachstehende Konfigurationstabelle zeigt eine Liste von Property-Pfad-Beispielen, um mehrere IAM-URIs zu definieren. Aus Gründen der Übersichtlichkeit wird das Präfix isy.security.oauth2.resourceserver.jwt in der unten aufgeführten Tabelle weggelassen.

Tabelle 6. Property-Pfad-Beispiele für Multi-IAM-URIs
Parameter Beschreibung

tenants.portal.issuer-uri tenants.portal.jwk-set-uri

Property-Set zur Definition einer externen und internen IAM-URI für das Portal

tenants.sgw-1.issuer-uri tenants.sgw-1.jwk-set-uri

Property-Set zur Definition einer externen und internen IAM-URI für das ServiceGateway-1

tenants.sgw-2.issuer-uri tenants.sgw-2.jwk-set-uri

Property-Set zur Definition zweier interner IAM-URIs für das ServiceGateway-2

tenants.batch.issuer-uri tenants.batch.jwk-set-uri

Property-Set zur Definition zweier interner IAM-URIs für den Batch

tenants.ga-A-1.issuer-uri tenants.ga-A-1.jwk-set-uri

Erstes Property-Set zur Definition einer externen und internen IAM-URI für die Geschäftsanwendung-A

tenants.ga-A-2.issuer-uri tenants.ga-A-2.jwk-set-uri

Zweites Property-Set zur Definition zweier interner IAM-URIs für die Geschäftsanwendung-A

tenants.ga-A-3.issuer-uri tenants.ga-A-3.jwk-set-uri

Drittes Property-Set zur weiteren Definition zweier interner IAM-URIs für die Geschäftsanwendung-A

6.5.1. Verwendung und Auswertung des Property-Sets

Property issuer-uri

Der Wert von issuer-uri wird gegen den Inhalt des iss-Claim des Access-Tokens validiert.

Property jwk-set-uri

Der Wert von jwk-set-uri des zugehörigen Tenants wird zum Laden der IAM-Konfiguration, z.B. des IAM-Zertifikats, eingesetzt, um im zweiten Schritt Token-Signatur gegen das IAM-Zertifikat zu validieren.

Wird nur der Parameter issuer-uri gesetzt, so wird dieser Wert auch für das Laden der IAM-Konfiguration genutzt.

6.6. 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 7. Konfiguration von Rollen und Rechten
Parameter Wertebereich Default Beschreibung

isy.security.role-privileges-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 11. 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.7. 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.8. 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 12. 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 eines der in der Secured-Annotation übergebenen Rechte besitzt.

Um konjunktive Verknüpfungen umzusetzen, sollte die Annotation @PreAuthorize aus Spring Security verwendet werden. @PreAuthorize ist für die Prüfung eines einzigen Rechtes äquivalent zu @Secured, nutzt aber die Spring Expression Language (SpEL).

Dadurch sind Ausdrücke möglich wie:

@PreAuthorize("hasAuthority('PRIV_Recht_A') and hasAuthority('PRIV_Recht_B')")

Die annotationsbasierte Autorisierung auf Methoden-/Klassenlevel muss durch setzen der @EnableMethodSecurity-Annotation an einer @Configuration-Klasse aktiviert werden. Dabei kann mit den Optionen securedEnabled und prePostEnabled die Verwendung von @Secured und @PreAuthorize aktiviert/deaktiviert werden. Die Option prePostEnabled ist standardmäßig aktiviert.

Listing 13. Aktivierung der Method Security
@Configuration
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfiguration {
    // ...
}

Method Level Security ist für die Nutzung in der Service Schicht. Die Verwendung an @Controller Klassen/Methoden kann zu Fehlern führen und wird nicht empfohlen. Für Webcontroller ohne Service-Schicht kann Request Level Autorisierung genutzt werden.

Das mehrfache setzen der @EnableMethodSecurity-Annotation sollte vermieden werden. Mit abweichender Konfiguration kann dies zu unerwartetem Verhalten und Fehlern führen.

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, direkt über den Spring Security Context oder mithilfe der IsySecurityTokenUtil Klasse 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 14. Zugriff auf das Behördenkennzeichen
String bhknz = (String) this.berechtigungsmanager.getTokenAttribute(BEARER_TOKEN_ATTR_BHKNZ);

Die Methode getTokenAttribute im Berechtigungsmanager ist deprecated und wird in zukünftigen Versionen entfernt.

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.
Zur Überprüfung, ob eine Authentication vom Typ AbstractOAuth2TokenAuthenticationToken im Security Context vorliegt, kann die Methode hasOAuth2Token() verwendet werden.

Die IsySecurityTokenUtil Klasse bietet statische Methoden zum Auslesen von Token-Claims:

  • getLogin()

    • Beschreibung: Gibt den Login des aktuellen Benutzers zurück.

    • Rückgabewert: Ein Optional<String>, das den Login des Benutzers enthält oder ein leeres Optional, wenn das Login-Attribut nicht gesetzt ist.

  • getUserId():

    • Beschreibung: Gibt die UserId des aktuellen Benutzers zurück.

    • Rückgabewert: Ein String, der die UserId oder, falls nicht gesetzt, den Subject Identifier (sub) enthält.

  • getBhknz():

    • Beschreibung: Gibt das Behördenkennzeichen (bhknz) des aktuellen Benutzers zurück.

    • Rückgabewert: Ein Optional<String>, das das Behördenkennzeichen enthält

  • getDisplayName():

    • Beschreibung: Gibt den Namen des aktuellen Benutzers zurück. Wenn der Name nicht gesetzt ist, wird der Login zurückgegeben.

    • Rückgabewert: Ein optionaler <String>, der den Namen oder, falls nicht gesetzt, den Login enthält.

  • getTokenAttribute(String key):

    • Beschreibung: Eine allgemeine Methode, um ein Attribut des Tokens abzurufen.

    • Rückgabewert: Das gesuchte Attribut des Tokens als Object. Sofern dieses nicht existiert, wird eine OAuth2AuthenticationException zurückgegeben.

Beispiel zum Zugriff auf das Behördenkennzeichen mithilfe der IsySecurityTokenUtil Klasse:

Listing 15. Zugriff auf das Behördenkennzeichen über IsySecurityTokenUtil
Optional<String> bhknz = IsySecurityTokenUtil.getBhknz();

Über die Methode hasTokenExpired(Duration expirationTimeOffset) kann die Gültigkeit für ein im Security Context liegendes AbstractOAuth2TokenAuthenticationToken abgefragt werden. Der Parameter expirationTimeOffset gibt eine Zeitspanne an, in der das Token vor dem eigentlichen Ablaufzeitpunkt bereits als abgelaufen gilt. Für den Fall, dass kein Wert für den Ablaufzeitpunkt im Token gesetzt ist, wird das Token als abgelaufen betrachtet und true zurückgegeben.

IsySecurityTokenUtil ermöglicht es, Standard-Claims mithilfe von zusätzlichen Properties um benutzerdefinierte Claims zu erweitern. Die entsprechenden Property-Konfigurationen sind in der Datei "isy-security-token.properties" im Verzeichnis "resources/config" hinterlegt. Bei speziellen Anforderungen ist es möglich, die Datei anzupassen, um die Claims entsprechend zu modifizieren.

Tabelle 8. Default Konfigurationsparameter der Claims
Parameter Wertebereich Default

isy.security.oauth2.claim.login

String

preferred_username

isy.security.oauth2.claim.userId

String

internekennung

isy.security.oauth2.claim.bhknz

String

bhknz

isy.security.oauth2.claim.displayName

String

name

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 16. 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 die Bibliothek isy-security-test beschrieben, die 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 17. Beispiel für die Erstellung des EmbeddedOidcProviderMock
public EmbeddedOidcProviderMock createMock() {
    EmbeddedOidcProviderMock mock =
            new EmbeddedOidcProviderMock("localhost", 9095, "/auth/realms/testrealm");

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

    return mock;
}

Detaillierte Informationen zur Verwendung des EmbeddedOidcProviderMock können der Dokumentation 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>