Nutzungsvorgaben Security
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.
Die Nutzung ist unter den Lizenzbedingungen der Creative Commons Namensnennung 4.0 International gestattet.
Java Bibliothek / IT-System
Name | Art | Version |
---|---|---|
|
Bibliothek |
v3.2.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' 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 denAuthentifizierungsmanager
enthält, odernull
falls die Anwendung als Resource-Server agiert und keinAuthentifizierungsmanager
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) undpassword
(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
undpassword
: 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 vorhandeneAbstractOAuth2TokenAuthenticationToken
abgelaufen ist. Über den ParameterexpirationTimeOffset
wird eine Zeitspanne angegeben, während der das Token bereits vor dem eigentlichen Ablaufzeitpunkt als abgelaufen gilt. Eine Authentifizierung mittelsauthentifiziere(String oauth2ClientRegistrationId)
wird ebenfalls dann vorgenommen, wenn keineAuthentication
vom TypAbstractOAuth2TokenAuthenticationToken
im SecurityContext vorliegt.Für den client_credentials
(Client Credentials Flow) GrantType erfolgt unabhängig vom ParameterexpirationTimeOffset
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 seinerclientId
undclientSecret
. Dasbhknz
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 derclientId
undclientSecret
des zu verwendenden OAuth 2.0 Clients und demusername
undpassword
des Resource Owners. Dasbhknz
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:
<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.
Parameter | Wertebereich | Beschreibung |
---|---|---|
|
|
Identifier der Client-Applikation im Autorisierungsserver. |
|
|
Secret, das nur der Anwendung und dem Autorisierungsserver bekannt ist. |
|
|
Methode, durch welche die Anwendung ein Bearer-Token erhält. Abhängig von dem zu verwendenden Flow auf |
|
|
(Optional) ID des zu verwendenden Client-Providers, falls diese nicht mit der Registration-ID übereinstimmt. |
|
|
Der OpenID Connect Issuer Identifier für die Konfiguration des Clients über den Discovery Endpoint, beispielsweise in der Form |
3.2.2. Isyfact spezifische Konfigurationsparameter
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:
Parameter | Wertebereich | Default | Beschreibung |
---|---|---|---|
|
|
|
Benutzername für Grant-Type |
|
|
|
Benutzerpassword für Grant-Type |
|
|
|
Behördenkennzeichen für Grant-Type |
|
|
|
Konfigurierbarer Headername für Zwei-Faktor-Authentifizierung. |
|
|
|
Organisational Unit, die in Verbindung mit bhknz verwendet wird, um den Wert für bhknz-header-name zu bilden. (Muss gesetzt werden, wenn eine |
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.
@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:
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:
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.
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.
-
Authentifizierung per
oauth2ClientRegistrationId
. Die Auswahl des Flows (Client Credentials oder Resource Owner Password Credentials) wird dabei über denauthorization-grant-type
der vollständig konfiguriertenClientRegistration
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. -
Authentifizierung per
issuerLocation
,clientId
undclientSecret
. Diese Methode nutzt den Client Credentials Flow und kann verwendet werden, wenn die Zugangsdaten des OAuth 2.0 Clients (clientId
undclientSecret
) erst zur Laufzeit bekannt sind. DieissuerLocation
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. -
Authentifizierung per
issuerLocation
,clientId
,clientSecret
,username
undpassword
. Diese Methode nutzt den Resource Owner Password Credentials Flow und kann verwendet werden, wenn die Zugangsdaten des technischen Nutzers (username
undpassword
des Resource Owners) erst zur Laufzeit bekannt sind. Die Zugangsdaten des Vermittlers (clientId
undclientSecret
) werden benötigt, um die Authentifizierungsabfrage selbst zu autorisieren. DieissuerLocation
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.
[...]
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.
@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:
<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:
@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();
}
}
Bei der Verwendung von isy-sicherheit
wurde der CSRF-Filter von Spring Security in vielen Fällen standarmäßig deaktiviert.
Damit die von der Anwendung bereitgestellten Schnittstellen weiterhin ohne Probleme erreicht werden können, muss dieser mglw. durch entsprechende Konfiguration der SecurityFilterChain
wieder deaktiviert werden: http.csrf().disable()
.
Dies soll nur falls absolut notwendig durchgeführt werden, um die Angriffsfläche der Anwendung nicht zu vergrößern.
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.
Parameter | Wertebereich | Default | Beschreibung |
---|---|---|---|
|
|
|
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.
Parameter | Wertebereich | Default | Beschreibung |
---|---|---|---|
|
|
|
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.
Parameter | Wertebereich | Default | Beschreibung |
---|---|---|---|
|
|
|
URI des Ausstellers des JWT, spezifisch für den Tenant |
|
|
|
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.
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.
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:
Parameter | Wertebereich | Default | Beschreibung |
---|---|---|---|
|
|
|
Pfad zu der XML-Datei, welche die Rollen-/Berechtigungszuordnungen enthält. |
Der Baustein liefert ein XML-Schema für den Aufbau der Konfigurationsdatei mit.
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:
<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.
@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')")
Standardmäßig aktiviert die isy-security AutoConfiguration Method Security durch die @EnableMethodSecurity
-Annotation mit der Option securedEnabled = true
.
Die Option prePostEnabled
ist per default aktiv.
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class IsyEnableMethodSecurityAutoConfiguration {
// ...
}
Sollte der Bedarf bestehen, die @EnableMethodSecurity
-Annotation zu überschreiben, die @EnableGlobalMethodSecurity
-Annotation zu verwenden oder Method Security zu deaktivieren, so wird empfohlen die entsprechende AutoConfiguration Klasse aus isy-security zu excludieren:
spring.autoconfigure.exclude=de.bund.bva.isyfact.security.autoconfigure.IsyEnableMethodSecurityAutoConfiguration
Method Level Security ist für die Nutzung in der Service Schicht.
Die Verwendung an Das mehrfache setzen der |
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.
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:
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.
Parameter | Wertebereich | Default |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
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.
@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:
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>