Freigeben über


Migrieren einer Anwendung zur Verwendung von kennwortlosen Verbindungen mit Azure Event Hubs für Kafka

In diesem Artikel wird erläutert, wie Sie von herkömmlichen Authentifizierungsmethoden zu sichereren, kennwortlosen Verbindungen mit Azure Event Hubs für Kafka migrieren.

Anwendungsanforderungen an Azure Event Hubs für Kafka müssen authentifiziert werden. Azure Event Hubs für Kafka bietet verschiedene Möglichkeiten, wie Apps eine sichere Verbindung herstellen können. Eine der Möglichkeiten besteht darin, eine Verbindungszeichenfolge zu verwenden. Sie sollten jedoch kennwortlose Verbindungen in Ihren Anwendungen priorisieren, wenn möglich.

Kennwortlose Verbindungen werden seit Spring Cloud Azure 4.3.0 unterstützt. Dieser Artikel ist ein Migrationshandbuch zum Entfernen von Anmeldeinformationen aus Spring Cloud Stream Kafka-Anwendungen.

Vergleichen von Authentifizierungsoptionen

Wenn sich die Anwendung bei Azure Event Hubs für Kafka authentifiziert, stellt sie eine autorisierte Entität bereit, um den Event Hubs-Namespace zu verbinden. Apache Kafka-Protokolle bieten mehrere SIMPLE Authentication and Security Layer (SASL)-Mechanismen für die Authentifizierung. Gemäß den SASL-Mechanismen gibt es zwei Authentifizierungsoptionen, mit denen Sie den Zugriff auf Ihre sicheren Ressourcen autorisieren können: die Microsoft Entra-Authentifizierung und die SAS-Authentifizierung (Shared Access Signature).

Microsoft Entra-Authentifizierung

Die Microsoft Entra-Authentifizierung ist ein Mechanismus zum Herstellen einer Verbindung mit Azure Event Hubs für Kafka mithilfe von In Microsoft Entra ID definierten Identitäten. Mit der Microsoft Entra-Authentifizierung können Sie Dienstprinzipalidentitäten und andere Microsoft-Dienste an einem zentralen Ort verwalten, wodurch die Berechtigungsverwaltung vereinfacht wird.

Die Verwendung der Microsoft Entra-ID für die Authentifizierung bietet die folgenden Vorteile:

  • Einheitliche Authentifizierung von Benutzern über Azure-Dienste hinweg.
  • Verwaltung von Kennwortrichtlinien und Kennwortrotation an einem zentralen Ort.
  • Mehrere Von Microsoft Entra ID unterstützte Authentifizierungsformen, die die Notwendigkeit zum Speichern von Kennwörtern vermeiden können.
  • Kunden können Event Hubs-Berechtigungen mithilfe externer Gruppen (Microsoft Entra ID) verwalten.
  • Unterstützung für die tokenbasierte Authentifizierung für Anwendungen, die eine Verbindung mit Azure Event Hubs für Kafka herstellen.

SAS-Authentifizierung

Event Hubs bietet außerdem SAS (Shared Access Signatures) für delegierten Zugriff auf Event Hubs für Kafka-Ressourcen.

Obwohl es möglich ist, eine Verbindung mit Azure Event Hubs für Kafka mit SAS herzustellen, sollte es mit Vorsicht verwendet werden. Sie müssen fleißig sein, die Verbindungszeichenfolge niemals an einem unsicheren Ort verfügbar zu machen. Jeder, der Zugriff auf die Verbindungszeichenfolge erhält, kann sich authentifizieren. So besteht beispielsweise das Risiko, dass ein böswilliger Benutzer auf die Anwendung zugreifen kann, wenn ein Verbindungszeichenfolge versehentlich in die Quellcodeverwaltung eingecheckt, über eine unsichere E-Mail gesendet, in den falschen Chat eingefügt oder von jemandem angezeigt wird, der nicht über die Berechtigung verfügen sollte. Stattdessen bietet die Autorisierung des Zugriffs mithilfe des tokenbasierten OAuth 2.0-Mechanismus eine überlegene Sicherheit und Benutzerfreundlichkeit gegenüber SAS. Erwägen Sie, Ihre Anwendung so zu aktualisieren, dass kennwortlose Verbindungen verwendet werden.

Einführung kennwortloser Verbindungen

Mit einer kennwortlosen Verbindung können Sie eine Verbindung mit Azure-Diensten herstellen, ohne Anmeldeinformationen im Anwendungscode, den zugehörigen Konfigurationsdateien oder in Umgebungsvariablen zu speichern.

Viele Azure-Dienste unterstützen kennwortlose Verbindungen, z. B. über Azure Managed Identity. Diese Techniken bieten robuste Sicherheitsfeatures, die Sie mithilfe von DefaultAzureCredential aus den Azure Identity-Clientbibliotheken implementieren können. In diesem Lernprogramm erfahren Sie, wie Sie eine vorhandene Anwendung aktualisieren, um sie anstelle von Alternativen wie Verbindungszeichenfolge zu verwendenDefaultAzureCredential.

DefaultAzureCredential unterstützt mehrere Authentifizierungsmethoden und bestimmt automatisch, welche Methode zur Laufzeit verwendet werden soll. Mit diesem Ansatz kann Ihre App unterschiedliche Authentifizierungsmethoden in verschiedenen Umgebungen (lokale Entwicklung gegenüber Produktion) verwenden, ohne umgebungsspezifischen Code zu implementieren.

Die Reihenfolge und Die Speicherorte, in denen nach Anmeldeinformationen gesucht wird, DefaultAzureCredential finden Sie in der Übersicht über die Azure Identity-Bibliothek. Wenn Sie beispielsweise lokal arbeiten, authentifiziert sich der Entwickler in der Regel mithilfe des Kontos, DefaultAzureCredential das der Entwickler für die Anmeldung bei Visual Studio verwendet hat. Wenn die App in Azure bereitgestellt wird, wechselt DefaultAzureCredential automatisch zur Verwendung einer verwalteten Identität. Für diesen Übergang sind keine Änderungen am Code erforderlich.

Um sicherzustellen, dass Verbindungen kennwortlos sind, müssen Sie sowohl die lokale Entwicklung als auch die Produktionsumgebung berücksichtigen. Wenn an beiden Stellen ein Verbindungszeichenfolge erforderlich ist, ist die Anwendung nicht kennwortlos.

In Ihrer lokalen Entwicklungsumgebung können Sie sich mit Azure CLI, Azure PowerShell, Visual Studio oder Azure-Plug-Ins für Visual Studio Code oder IntelliJ authentifizieren. In diesem Fall können Sie diese Anmeldeinformationen in Ihrer Anwendung verwenden, anstatt Eigenschaften zu konfigurieren.

Wenn Sie Anwendungen in einer Azure-Hostingumgebung bereitstellen, z. B. einem virtuellen Computer, können Sie verwaltete Identitäten in dieser Umgebung zuweisen. Anschließend müssen Sie keine Anmeldeinformationen angeben, um eine Verbindung mit Azure-Diensten herzustellen.

Hinweis

Eine verwaltete Identität bietet eine Sicherheitsidentität zur Darstellung einer App oder eines Diensts. Da die Identität von der Azure-Plattform verwaltet wird, müssen Sie keine Geheimnisse bereitstellen oder rotieren. Weitere Informationen zu verwalteten Identitäten finden Sie in der Übersichtsdokumentation.

Migrieren einer vorhandenen Anwendung zur Verwendung kennwortloser Verbindungen

In den folgenden Schritten wird erläutert, wie Sie eine vorhandene Anwendung migrieren, um kennwortlose Verbindungen anstelle einer SAS-Lösung zu verwenden.

0) Vorbereiten der Arbeitsumgebung für die lokale Entwicklungsauthentifizierung

Richten Sie zunächst mithilfe des folgenden Befehls einige Umgebungsvariablen ein.

export AZ_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
export AZ_EVENTHUBS_NAMESPACE_NAME=<YOUR_EVENTHUBS_NAMESPACE_NAME>
export AZ_EVENTHUB_NAME=<YOUR_EVENTHUB_NAME>

Ersetzen Sie die Platzhalter durch die folgenden Werte, die in diesem Artikel verwendet werden:

  • <YOUR_RESOURCE_GROUP>: Der Name der Ressourcengruppe, die Sie verwenden werden.
  • <YOUR_EVENTHUBS_NAMESPACE_NAME>: Der Name des Azure Event Hubs-Namespace, den Sie verwenden.
  • <YOUR_EVENTHUB_NAME>: Der Name des Ereignishubs, den Sie verwenden.

1) Erteilen der Berechtigung für Azure Event Hubs

Wenn Sie dieses Beispiel lokal mit der Microsoft Entra-Authentifizierung ausführen möchten, stellen Sie sicher, dass Sich Ihr Benutzerkonto über das Azure Toolkit für IntelliJ, das Visual Studio Code Azure-Konto-Plug-In oder die Azure CLI authentifiziert hat. Stellen Sie außerdem sicher, dass dem Konto ausreichende Berechtigungen erteilt wurden.

  1. Navigieren Sie im Azure-Portal zu Ihrem Event Hubs-Namespace. Verwenden Sie dazu entweder die Hauptsuchleiste oder die linke Navigationsleiste.

  2. Wählen Sie auf der Übersichtsseite von Event Hubs im linken Menü die Zugriffssteuerung (IAM) aus.

  3. Wählen Sie auf der Seite Zugriffssteuerung (IAM) die Registerkarte Rollenzuweisungen aus.

  4. Wählen Sie im oberen Menü "Hinzufügen" und dann "Rollenzuweisung hinzufügen" aus dem resultierenden Dropdownmenü aus.

    Screenshot der Seite Azure-Portal Access Control (IAM) der Event Hubs Namespace-Ressource mit hervorgehobener Option

  5. Über das Suchfeld können Sie die Ergebnisse für die gewünschte Rolle filtern. Suchen Sie in diesem Beispiel nach Azure Event Hubs Data Sender und Azure Event Hubs Data Receiver, und wählen Sie das übereinstimmende Ergebnis aus, und wählen Sie dann "Weiter" aus.

  6. Wählen Sie unter "Zugriff zuweisen" "Benutzer", "Gruppe" oder "Dienstprinzipal" und dann "Mitglieder auswählen" aus.

  7. Suchen Sie im Dialogfeld nach Ihrem Microsoft Entra-Benutzernamen (normalerweise Ihre E-Mail-Adresse benutzer@domäne), und wählen Sie unten im Dialogfeld Auswählen aus.

  8. Wählen Sie Überprüfen und zuweisen aus, um zur letzten Seite zu gelangen, und wählen Sie erneut Überprüfen und zuweisen aus, um den Vorgang abzuschließen.

Weitere Informationen zum Gewähren von Zugriffsrollen finden Sie unter Autorisieren des Zugriffs auf Event Hubs-Ressourcen mithilfe der Microsoft Entra-ID.

2) Melden Sie sich an, und migrieren Sie den App-Code, um kennwortlose Verbindungen zu verwenden.

Stellen Sie für die lokale Entwicklung sicher, dass Sie mit demselben Microsoft Entra-Konto authentifiziert sind, dem Sie die Rolle auf Ihren Event Hubs zugewiesen haben. Sie können sich über die Azure CLI, Visual Studio, Azure PowerShell oder andere Tools wie IntelliJ authentifizieren.

Melden Sie sich mit dem folgenden Befehl bei Azure über die Azure CLI an:

az login

Führen Sie als Nächstes die folgenden Schritte aus, um Ihre Spring Kafka-Anwendung so zu aktualisieren, dass kennwortlose Verbindungen verwendet werden. Obwohl konzeptionell ähnlich, verwendet jedes Framework unterschiedliche Implementierungsdetails.

  1. Öffnen Sie in Ihrem Projekt die datei pom.xml , und fügen Sie den folgenden Verweis hinzu:

    <dependency>
       <groupId>com.azure</groupId>
       <artifactId>azure-identity</artifactId>
       <version>1.6.0</version>
    </dependency>
    
  2. Implementieren Sie nach der Migration AuthenticateCallbackHandler und OAuthBearerToken in Ihrem Projekt für die OAuth2-Authentifizierung, wie im folgenden Beispiel gezeigt.

    public class KafkaOAuth2AuthenticateCallbackHandler implements AuthenticateCallbackHandler {
    
       private static final Duration ACCESS_TOKEN_REQUEST_BLOCK_TIME = Duration.ofSeconds(30);
       private static final String TOKEN_AUDIENCE_FORMAT = "%s://%s/.default";
    
       private Function<TokenCredential, Mono<OAuthBearerTokenImp>> resolveToken;
       private final TokenCredential credential = new DefaultAzureCredentialBuilder().build();
    
       @Override
       public void configure(Map<String, ?> configs, String mechanism, List<AppConfigurationEntry> jaasConfigEntries) {
          TokenRequestContext request = buildTokenRequestContext(configs);
          this.resolveToken = tokenCredential -> tokenCredential.getToken(request).map(OAuthBearerTokenImp::new);
       }
    
       private TokenRequestContext buildTokenRequestContext(Map<String, ?> configs) {
          URI uri = buildEventHubsServerUri(configs);
          String tokenAudience = buildTokenAudience(uri);
    
          TokenRequestContext request = new TokenRequestContext();
          request.addScopes(tokenAudience);
          return request;
       }
    
       @SuppressWarnings("unchecked")
       private URI buildEventHubsServerUri(Map<String, ?> configs) {
          String bootstrapServer = Arrays.asList(configs.get(BOOTSTRAP_SERVERS_CONFIG)).get(0).toString();
          bootstrapServer = bootstrapServer.replaceAll("\\[|\\]", "");
          URI uri = URI.create("https://" + bootstrapServer);
          return uri;
       }
    
       private String buildTokenAudience(URI uri) {
          return String.format(TOKEN_AUDIENCE_FORMAT, uri.getScheme(), uri.getHost());
       }
    
       @Override
       public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
          for (Callback callback : callbacks) {
             if (callback instanceof OAuthBearerTokenCallback) {
                OAuthBearerTokenCallback oauthCallback = (OAuthBearerTokenCallback) callback;
                this.resolveToken
                        .apply(credential)
                        .doOnNext(oauthCallback::token)
                        .doOnError(throwable -> oauthCallback.error("invalid_grant", throwable.getMessage(), null))
                        .block(ACCESS_TOKEN_REQUEST_BLOCK_TIME);
             } else {
                throw new UnsupportedCallbackException(callback);
             }
          }
       }
    
       @Override
       public void close() {
          // NOOP
       }
    }
    
    public class OAuthBearerTokenImp implements OAuthBearerToken {
        private final AccessToken accessToken;
        private final JWTClaimsSet claims;
    
        public OAuthBearerTokenImp(AccessToken accessToken) {
            this.accessToken = accessToken;
            try {
                claims = JWTParser.parse(accessToken.getToken()).getJWTClaimsSet();
            } catch (ParseException exception) {
                throw new SaslAuthenticationException("Unable to parse the access token", exception);
            }
        }
    
        @Override
        public String value() {
            return accessToken.getToken();
        }
    
        @Override
        public Long startTimeMs() {
            return claims.getIssueTime().getTime();
        }
    
        @Override
        public long lifetimeMs() {
            return claims.getExpirationTime().getTime();
        }
    
        @Override
        public Set<String> scope() {
            // Referring to https://docs.microsoft.com/azure/active-directory/develop/access-tokens#payload-claims, the scp
            // claim is a String, which is presented as a space separated list.
            return Optional.ofNullable(claims.getClaim("scp"))
                    .map(s -> Arrays.stream(((String) s)
                    .split(" "))
                    .collect(Collectors.toSet()))
                    .orElse(null);
        }
    
        @Override
        public String principalName() {
            return (String) claims.getClaim("upn");
        }
    
        public boolean isExpired() {
            return accessToken.isExpired();
        }
    }
    
  3. Wenn Sie Ihren Kafka-Produzenten oder -Consumer erstellen, fügen Sie die konfiguration hinzu, die erforderlich ist, um den SASL/OAUTHBEARER-Mechanismus zu unterstützen. Die folgenden Beispiele zeigen, wie Ihr Code vor und nach der Migration aussehen soll. Ersetzen Sie in beiden Beispielen den <eventhubs-namespace> Platzhalter durch den Namen Ihres Event Hubs-Namespaces.

    Vor der Migration sollte Ihr Code wie im folgenden Beispiel aussehen:

    Properties properties = new Properties();
    properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, "<eventhubs-namespace>.servicebus.windows.net:9093");
    properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
    properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
    properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
    properties.put(SaslConfigs.SASL_MECHANISM, "PLAIN");
    properties.put(SaslConfigs.SASL_JAAS_CONFIG,
            String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"$ConnectionString\" password=\"%s\";", connectionString));
    return new KafkaProducer<>(properties);
    

    Nach der Migration sollte Ihr Code wie im folgenden Beispiel aussehen. Ersetzen Sie in diesem Beispiel den <path-to-your-KafkaOAuth2AuthenticateCallbackHandler> Platzhalter durch den vollständigen Klassennamen für Ihre implementierte Klasse KafkaOAuth2AuthenticateCallbackHandler.

    Properties properties = new Properties();
    properties.put(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, "<eventhubs-namespace>.servicebus.windows.net:9093");
    properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
    properties.put(SaslConfigs.SASL_MECHANISM, "OAUTHBEARER");
    properties.put(SaslConfigs.SASL_JAAS_CONFIG, "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required");
    properties.put(SaslConfigs.SASL_LOGIN_CALLBACK_HANDLER_CLASS, "<path-to-your-KafkaOAuth2AuthenticateCallbackHandler>");
    return new KafkaProducer<>(properties);
    

Lokales Ausführen der App

Führen Sie die Anwendung lokal aus, nachdem Sie diese Codeänderungen vorgenommen haben. Die neue Konfiguration sollte Ihre lokalen Anmeldeinformationen abrufen, vorausgesetzt, Sie sind bei einer kompatiblen IDE oder einem Befehlszeilentool angemeldet, z. B. azure CLI, Visual Studio oder IntelliJ. Die Rollen, die Sie Ihrem lokalen Entwicklerbenutzer in Azure zugewiesen haben, ermöglichen es Ihrer App, eine lokale Verbindung mit dem Azure-Dienst herzustellen.

3) Konfigurieren der Azure-Hostingumgebung

Nachdem Ihre Anwendung für die Verwendung kennwortloser Verbindungen konfiguriert und lokal ausgeführt wird, kann sich derselbe Code bei Azure-Diensten authentifizieren, nachdem sie in Azure bereitgestellt wurde. Beispielsweise kann eine Anwendung, die in einer Azure Spring Apps-Instanz bereitgestellt wird, die über eine verwaltete Identität verfügt, eine Verbindung mit Azure Event Hubs für Kafka herstellen.

In diesem Abschnitt führen Sie zwei Schritte aus, um die Ausführung Ihrer Anwendung in einer Azure-Hostingumgebung auf kennwortlose Weise zu ermöglichen:

  • Weisen Sie die verwaltete Identität für Ihre Azure-Hostingumgebung zu.
  • Weisen Sie der verwalteten Identität Rollen zu.

Hinweis

Azure stellt auch Service Connector bereit, der Ihnen dabei helfen kann, Ihren Hostingdienst mit Event Hubs zu verbinden. Mit Service Connector zum Konfigurieren Ihrer Hostingumgebung können Sie den Schritt zum Zuweisen von Rollen zu Ihrer verwalteten Identität weglassen, da Service Connector dies für Sie tut. Im folgenden Abschnitt wird beschrieben, wie Sie Ihre Azure-Hostingumgebung auf zwei Arten konfigurieren: eine über Service Connector und die andere, indem Sie jede Hostingumgebung direkt konfigurieren.

Wichtig

Die Befehle von Service Connector erfordern Azure CLI 2.41.0 oder höher.

Zuweisen der verwalteten Identität für Ihre Azure-Hostingumgebung

Die folgenden Schritte zeigen, wie Sie eine vom System zugewiesene verwaltete Identität für verschiedene Webhostingdienste zuweisen. Die verwaltete Identität kann mithilfe der zuvor eingerichteten App-Konfigurationen auf sichere Weise eine Verbindung mit anderen Azure-Diensten herstellen.

  1. Wählen Sie auf der Hauptübersichtsseite Ihrer Azure-App Dienstinstanz die Option "Identität" im Navigationsbereich aus.

  2. Stellen Sie auf der Registerkarte "System zugewiesen" sicher, dass das Feld "Status" aktiviert ist. Eine systemseitig zugewiesene Identität wird von Azure intern verwaltet und führt administrative Aufgaben für Sie aus. Die Details und IDs der Identität werden in Ihrem Code nie offengelegt.

Sie können auch verwaltete Identitäten in einer Azure-Hostingumgebung mithilfe der Azure CLI zuweisen.

Sie können einer Azure-App Dienstinstanz eine verwaltete Identität mit dem Befehl az webapp identity assign zuweisen, wie im folgenden Beispiel gezeigt.

export AZURE_MANAGED_IDENTITY_ID=$(az webapp identity assign \
    --resource-group $AZ_RESOURCE_GROUP \
    --name <app-service-name> \
    --query principalId \
    --output tsv)

Zuweisen einer Rolle zur verwalteten Identität

Erteilen Sie als Nächstes Berechtigungen für die verwaltete Identität, die Sie erstellt haben, um auf Ihren Event Hubs-Namespace zuzugreifen. Sie können Berechtigungen erteilen, indem Sie der verwalteten Identität eine Rolle zuweisen, genau wie bei Ihrem lokalen Entwicklungsbenutzer.

Wenn Sie Ihre Dienste über den Service Connector verbunden haben, müssen Sie diesen Schritt nicht ausführen. Die folgenden erforderlichen Konfigurationen wurden für Sie behandelt:

  • Wenn Sie beim Erstellen der Verbindung eine verwaltete Identität ausgewählt haben, wurde eine vom System zugewiesene verwaltete Identität für Ihre App erstellt und den Azure Event Hubs Data Sender - und Azure Event Hubs-Datenempfängerrollen auf den Event Hubs zugewiesen.

  • Wenn Sie ein Verbindungszeichenfolge verwenden, wurde die Verbindungszeichenfolge als App-Umgebungsvariable hinzugefügt.

Testen der App

Nachdem Sie diese Codeänderungen vorgenommen haben, navigieren Sie im Browser zu Ihrer gehosteten Anwendung. Ihre App sollte eine erfolgreiche Verbindung mit den Azure Event Hubs für Kafka herstellen können. Denken Sie daran, dass es einige Minuten dauern kann, bis die Rollenzuweisungen in der Azure-Umgebung weitergegeben werden. Ihre Anwendung ist jetzt so konfiguriert, dass sie lokal und in einer Produktionsumgebung ausgeführt werden kann, ohne dass die Entwickler Geheimnisse in der Anwendung selbst verwalten müssen.

Nächste Schritte

In diesem Tutorial haben Sie gelernt, wie Sie eine Anwendung zu kennwortlosen Verbindungen migrieren.

Sie können die folgenden Ressourcen lesen, um die in diesem Artikel erläuterten Konzepte ausführlicher zu erkunden: