java - 如何使用基于证书的身份验证创建 Azure blob n 使用 java 下载文件
问题描述
我编写了 java 程序,它连接到我创建的 Azure blob 存储并使用下面的程序下载文件内容,但我的实际产品场景不同,客户端共享了 ThumbPrint、ClientId、AzureKeyVaultUrl、SecretId 和 containerName 以及证书。我的程序在我创建的具有试用期的帐户上运行良好。但是不明白如何使用基于证书的身份验证创建帐户,在使用 java 程序连接时使用该帐户。包com;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Scanner;
public class ConnectToAzureToExistingContainer {
public static final String storageConnectionString =
"DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=yyy;EndpointSuffix=zzzz";
public static void main( String[] args )
{
CloudStorageAccount storageAccount;
CloudBlobClient blobClient = null;
CloudBlobContainer container=null;
try {
storageAccount = CloudStorageAccount.parse(storageConnectionString);
blobClient = storageAccount.createCloudBlobClient();
container = blobClient.getContainerReference("revenuestream");
CloudBlockBlob blob = container.getBlockBlobReference("revenuestreams.csv");
System.out.println(blob.downloadText());
System.out.println("Done...");
}
catch (StorageException ex){
System.out.println(String.format("Error returned from the service. Http code: %d and error code: %s", ex.getHttpStatusCode(), ex.getErrorCode()));
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
解决方案
正如@Thomas 所说,您可以使用 Azure AD 获取访问令牌来访问您的存储文件。主要机制可以参考这个文档。您需要获取访问令牌来调用存储 API以访问您的存储服务。并且您想使用证书来获取此访问令牌以访问存储服务。您可以按照以下步骤操作。
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("path of your .pfx file","password of your .pfx file",'Exportable')
$x5t = [System.Convert]::ToBase64String($cer.GetCertHash())
$x5t
将 .pfx 文件转换为 .der 文件,以便我们可以在 Java 中轻松使用它:
1)从 pfx 转换 Pem 文件:
openssl pkcs12 -in "你的 .pfx 文件路径" -out "新的 .pem 文件路径" -clcerts
2)将 Pem 转换为 der 以便 java 可以轻松读取它:
openssl pkcs8 -topk8 -inform PEM -outform DER -in "pem 文件路径" -out "new der 文件路径" -nocrypt
请按照以下代码从 Azure AD 获取访问令牌:
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.naming.ServiceUnavailableException;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientAssertion;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class PublicClient {
private final static String TENANT_ID = "your tanant id";
private final static String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID;
private final static String CLIENT_ID = "your Azure AD app ID";
private final static String X5TVALUE_STRING = "x5t value we get from step2 ";
private final static String DERFILE_PATH_STRING = "der file path";
public static void main(String args[]) throws Exception {
// Request access token from AAD
AuthenticationResult result = getAccessToken();
System.out.print(result.getAccessToken());
}
public static PrivateKey getPrivateKey() throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(DERFILE_PATH_STRING));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
private static AuthenticationResult getAccessToken() throws Exception {
AuthenticationContext context;
AuthenticationResult result;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
PrivateKey key = getPrivateKey();
String jwt = Jwts.builder().setHeaderParam("typ", "JWT").setHeaderParam("alg", "RS256")
.setHeaderParam("x5t",X5TVALUE_STRING).setSubject(CLIENT_ID)
.setExpiration(new Date(System.currentTimeMillis() + 200000)).setIssuer(CLIENT_ID)
.setNotBefore(new Date())
.setAudience("https://login.microsoftonline.com/" + TENANT_ID + "/oauth2/token")
.setId(UUID.randomUUID().toString()).signWith(SignatureAlgorithm.RS256, key).compact();
ClientAssertion clientAssertion = new ClientAssertion(jwt);
Future<AuthenticationResult> future = context.acquireToken("https://storage.azure.com/", clientAssertion,
null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException("authentication result was null");
}
return result;
}
}
使用此令牌,我们可以调用 storage REST API,请注意,如果您使用 Azure AD auth 访问您的存储,则请求标头中需要“x-ms-version: 2017-11-09”:
推荐阅读
- c# - 手动添加时更改 TableLayoutPanel 的颜色
- graphics - 手动转换交换链图像布局
- javascript - Map 方法不断返回一个对象数组,导致 React 出错
- bash - 捕获多行 ssh 命令的输出
- android - 存储在特定文件夹 SharedPreferences 颤动
- javascript - 如何验证字符串是否只有一个表情符号?
- oracle - 如何在 oracle Apex 的交互式报表中将行数设置为 5
- python - 无法在pygame中检测到碰撞
- bash - 使用 bash 脚本编辑 .tf 变量文件
- text - 如何在矩形内剪辑 vega-lite 文本?