Consolidate all Java RBAC sample work (#456)
* Adding Java RBAC sample * Remove unwanted classpath and project files * README with RBAC and MI instructions
This commit is contained in:
Родитель
1a9a5401c4
Коммит
c05dba7e76
|
@ -0,0 +1,42 @@
|
|||
# Role based access sample with Azure Event Hubs Java SDK
|
||||
|
||||
For general information on using Role based access (RBAC) with Azure Event Hubs, see the [documentation](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-role-based-access-control).
|
||||
|
||||
This sample uses the [Microsoft Authentication Library (MSAL) for Java](https://github.com/AzureAD/microsoft-authentication-library-for-java) to obtain tokens from Azure Active Directory (AAD).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Please refer to the Java sample overview README for setting up the sample environment, including creating an Event Hubs cloud namespace and an event hub.
|
||||
|
||||
The specific AAD pattern used in this sample is ["Authenticate an appliction"](https://docs.microsoft.com/en-us/azure/event-hubs/authenticate-application). Please follow the steps described to
|
||||
create an application (client) id and application (client) secret, obtain your directory (tenant) id, and assign the application the Data Owner role on your event hub.
|
||||
|
||||
Once you have performed the previous steps, edit SendReceive.java to provide the necessary information.
|
||||
|
||||
```java
|
||||
final java.net.URI namespace = new java.net.URI("----EventHubsNamespace---.servicebus.windows.net"); // to target National clouds, change domain name too
|
||||
final String eventhub = "----EventHubName----";
|
||||
final String authority = "https://login.windows.net/----replaceWithTenantIdGuid----";
|
||||
final String clientId = "----replaceWithClientIdGuid----"; // not needed to run with Managed Identity
|
||||
final String clientSecret = "----replaceWithClientSecret----"; // not needed to run with Managed Identity
|
||||
```
|
||||
|
||||
The Azure Event Hubs Java SDK also has limited built-in support for Managed Identity: specifically, when running in a virtual machine with a system-assigned managed identity, the SDK can
|
||||
obtain and use that identity to perform role based access. This sample can demonstrate that ability when run in an
|
||||
[appropriately-configured virtual machine](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm) and the managed identity
|
||||
[has been assigned the Data Owner role on your event hub.](https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-managed-service-identity)
|
||||
|
||||
|
||||
## Build and run
|
||||
|
||||
The sample can be built independently with
|
||||
|
||||
```bash
|
||||
mvn clean package
|
||||
```
|
||||
|
||||
and then run with (or just from VS Code or another Java IDE)
|
||||
|
||||
```bash
|
||||
java -jar ./target/send-1.0.0-jar-with-dependencies.jar
|
||||
```
|
|
@ -0,0 +1,63 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>rbac</groupId>
|
||||
<version>1.0.0</version>
|
||||
<artifactId>rbac</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.6.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<debug>true</debug>
|
||||
<debuglevel>lines,vars,source</debuglevel>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.microsoft.azure.eventhubs.samples.rbac.SendReceive</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>azure-eventhubs</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>msal4j</artifactId>
|
||||
<version>0.5.0-preview</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.eventhubs.samples.rbac;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.microsoft.aad.msal4j.*;
|
||||
import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider;
|
||||
|
||||
class AuthCallback implements AzureActiveDirectoryTokenProvider.AuthenticationCallback {
|
||||
final private String clientId;
|
||||
final private String clientSecret;
|
||||
|
||||
public AuthCallback(final String clientId, final String clientSecret) {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> acquireToken(String audience, String authority, Object state) {
|
||||
try {
|
||||
ConfidentialClientApplication app = ConfidentialClientApplication.builder(this.clientId, new ClientSecret(this.clientSecret))
|
||||
.authority(authority)
|
||||
.build();
|
||||
ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience + ".default")).build();
|
||||
return app.acquireToken(parameters).thenApply((authResult) -> {
|
||||
return authResult.accessToken();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
CompletableFuture<String> failed = new CompletableFuture<String>();
|
||||
failed.completeExceptionally(e);
|
||||
return failed;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.eventhubs.samples.rbac;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
import com.microsoft.aad.msal4j.*;
|
||||
import com.microsoft.azure.eventhubs.*;
|
||||
import com.microsoft.azure.eventhubs.impl.ClientConstants;
|
||||
|
||||
class CustomTokenProvider implements ITokenProvider {
|
||||
final private String authority;
|
||||
final private String audience = ClientConstants.EVENTHUBS_AUDIENCE;
|
||||
final private String clientId;
|
||||
final private String clientSecret;
|
||||
|
||||
public CustomTokenProvider(final String authority, final String clientId, final String clientSecret) {
|
||||
this.authority = authority;
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SecurityToken> getToken(String resource, Duration timeout) {
|
||||
try {
|
||||
ConfidentialClientApplication app = ConfidentialClientApplication.builder(this.clientId, new ClientSecret(this.clientSecret))
|
||||
.authority(authority)
|
||||
.build();
|
||||
ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience + ".default")).build();
|
||||
return app.acquireToken(parameters)
|
||||
.thenApply((authResult) -> {
|
||||
try {
|
||||
return new JsonSecurityToken(authResult.accessToken(), resource);
|
||||
} catch (ParseException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
CompletableFuture<SecurityToken> failed = new CompletableFuture<SecurityToken>();
|
||||
failed.completeExceptionally(e);
|
||||
return failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
|
||||
package com.microsoft.azure.eventhubs.samples.rbac;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider;
|
||||
import com.microsoft.azure.eventhubs.ConnectionStringBuilder;
|
||||
import com.microsoft.azure.eventhubs.EventData;
|
||||
import com.microsoft.azure.eventhubs.EventHubClient;
|
||||
import com.microsoft.azure.eventhubs.EventHubException;
|
||||
import com.microsoft.azure.eventhubs.EventPosition;
|
||||
import com.microsoft.azure.eventhubs.PartitionReceiver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class SendReceive {
|
||||
|
||||
final java.net.URI namespace = new java.net.URI("----EventHubsNamespace---.servicebus.windows.net"); // to target National clouds, change domain name too
|
||||
final String eventhub = "----EventHubName----";
|
||||
final String authority = "https://login.windows.net/----replaceWithTenantIdGuid----";
|
||||
final String clientId = "----replaceWithClientIdGuid----"; // not needed to run with Managed Identity
|
||||
final String clientSecret = "----replaceWithClientSecret----"; // not needed to run with Managed Identity
|
||||
|
||||
public SendReceive() throws URISyntaxException {
|
||||
}
|
||||
|
||||
public static int main(String[] args)
|
||||
throws EventHubException, ExecutionException, InterruptedException, IOException, URISyntaxException {
|
||||
|
||||
SendReceive ss = new SendReceive();
|
||||
return ss.run(args);
|
||||
}
|
||||
|
||||
private int run(String[] args) throws IOException {
|
||||
|
||||
System.out.println("Choose an action:");
|
||||
System.out.println("[A] Authenticate via Managed Identity");
|
||||
System.out.println("[B] Get AAD token using AuthCallback");
|
||||
System.out.println("[C] Get AAD token using AzureActiveDirectoryTokenProvider to wrap AuthCallback");
|
||||
System.out.println("[D] Get AAD token using ITokenProvider implementation");
|
||||
|
||||
char key = (char)System.in.read();
|
||||
char keyPressed = Character.toUpperCase(key);
|
||||
|
||||
try {
|
||||
switch (keyPressed) {
|
||||
case 'A':
|
||||
managedIdentityScenario();
|
||||
break;
|
||||
case 'B':
|
||||
useAuthCallback();
|
||||
break;
|
||||
case 'C':
|
||||
useAADTokenProvider();
|
||||
break;
|
||||
case 'D':
|
||||
useCustomTokenProvider();
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unknown command, press enter to exit");
|
||||
System.in.read();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
System.out.println("Error during execution. Exception: " + ex.toString());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private ScheduledExecutorService getScheduledExecutorService() {
|
||||
// The Executor handles all asynchronous tasks and this is passed to the EventHubClient instance.
|
||||
// This enables the user to segregate their thread pool based on the work load.
|
||||
// This pool can then be shared across multiple EventHubClient instances.
|
||||
// The following sample uses a single thread executor, as there is only one EventHubClient instance,
|
||||
// handling different flavors of ingestion to Event Hubs here.
|
||||
final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(4);
|
||||
return executorService;
|
||||
}
|
||||
|
||||
private void managedIdentityScenario() throws IOException, EventHubException {
|
||||
|
||||
final ConnectionStringBuilder connStr = new ConnectionStringBuilder()
|
||||
.setEndpoint(this.namespace)
|
||||
.setEventHubName(this.eventhub)
|
||||
.setAuthentication(ConnectionStringBuilder.MANAGED_IDENTITY_AUTHENTICATION);
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
final EventHubClient ehClient = EventHubClient.createFromConnectionStringSync(connStr.toString(), executorService);
|
||||
|
||||
sendReceive(ehClient, executorService);
|
||||
}
|
||||
|
||||
private void useAuthCallback() throws IOException, InterruptedException, ExecutionException, EventHubException {
|
||||
|
||||
final AuthCallback callback = new AuthCallback(clientId, clientSecret);
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
final EventHubClient ehClient = EventHubClient.createWithAzureActiveDirectory(namespace, eventhub, callback, authority, executorService, null).get();
|
||||
|
||||
sendReceive(ehClient, executorService);
|
||||
}
|
||||
|
||||
private void useAADTokenProvider() throws IOException, InterruptedException, ExecutionException, EventHubException {
|
||||
|
||||
final AuthCallback callback = new AuthCallback(clientId, clientSecret);
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
final AzureActiveDirectoryTokenProvider aadTokenProvider = new AzureActiveDirectoryTokenProvider(callback, authority, null);
|
||||
|
||||
final EventHubClient ehClient = EventHubClient.createWithTokenProvider(namespace, eventhub, aadTokenProvider, executorService, null).get();
|
||||
|
||||
sendReceive(ehClient, executorService);
|
||||
}
|
||||
|
||||
private void useCustomTokenProvider() throws IOException, InterruptedException, ExecutionException, EventHubException {
|
||||
|
||||
final CustomTokenProvider tokenProvider = new CustomTokenProvider(authority, clientId, clientSecret);
|
||||
ScheduledExecutorService executorService = getScheduledExecutorService();
|
||||
|
||||
final EventHubClient ehClient = EventHubClient.createWithTokenProvider(namespace, eventhub, tokenProvider, executorService, null).get();
|
||||
|
||||
sendReceive(ehClient, executorService);
|
||||
}
|
||||
|
||||
private void sendReceive(EventHubClient ehClient, ScheduledExecutorService executorService) throws IOException, EventHubException {
|
||||
try {
|
||||
final Gson gson = new GsonBuilder().create();
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
|
||||
String payload = "Message " + Integer.toString(i);
|
||||
byte[] payloadBytes = gson.toJson(payload).getBytes(Charset.defaultCharset());
|
||||
EventData sendEvent = EventData.create(payloadBytes);
|
||||
ehClient.sendSync(sendEvent);
|
||||
}
|
||||
|
||||
System.out.println(Instant.now() + ": Send Complete...");
|
||||
|
||||
final PartitionReceiver receiver = ehClient.createReceiverSync(
|
||||
EventHubClient.DEFAULT_CONSUMER_GROUP_NAME,
|
||||
"0",
|
||||
EventPosition.fromStartOfStream());
|
||||
|
||||
Iterable<EventData> receivedEvents = receiver.receiveSync(100);
|
||||
while (true) {
|
||||
int batchSize = 0;
|
||||
if (receivedEvents != null) {
|
||||
for (final EventData receivedEvent : receivedEvents) {
|
||||
if (receivedEvent.getBytes() != null)
|
||||
System.out.println(String.format("Message Payload: %s", new String(receivedEvent.getBytes(), Charset.defaultCharset())));
|
||||
|
||||
System.out.println(String.format("Offset: %s, SeqNo: %s, EnqueueTime: %s",
|
||||
receivedEvent.getSystemProperties().getOffset(),
|
||||
receivedEvent.getSystemProperties().getSequenceNumber(),
|
||||
receivedEvent.getSystemProperties().getEnqueuedTime()));
|
||||
batchSize++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
System.out.println(String.format("ReceivedBatch Size: %s", batchSize));
|
||||
receivedEvents = receiver.receiveSync(100);
|
||||
}
|
||||
|
||||
System.out.println(Instant.now() + ": Receive Complete...");
|
||||
|
||||
System.out.println("Press Enter to stop.");
|
||||
System.in.read();
|
||||
} finally {
|
||||
ehClient.closeSync();
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче