如何使用 msal4j 和 Azure Blob 存储库更新令牌

How to renew token using msal4j and Azure Blob Storage library

我们使用 Microsoft 的 Java 库访问 Azure Blob 存储,并使用 msal4j 库使用 OAuth2 客户端访问模式获取访问令牌。下面的片段:

StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(account_name, generateOAuthToken());
storageAccount = new CloudStorageAccount(storageCredentialsToken, true);

private String generateOAuthToken() throws MalformedURLException {
  String authority = String.format(ACTIVE_DIRECTORY_ENDPOINT + "/%s/oauth2/v2.0/token", tenant_id);
  Set<String> scope = Collections.singleton(String.format("https://%s.blob.core.windows.net/.default", account_name));
  IClientCredential credential = ClientCredentialFactory.createFromSecret(client_secret);
  ConfidentialClientApplication cca = ConfidentialClientApplication
      .builder(client_id, credential)
      .authority(authority)
      .build();
  ClientCredentialParameters parameters = ClientCredentialParameters
      .builder(scope)
      .build();
  IAuthenticationResult result = cca.acquireToken(parameters).join();
  return result.accessToken();
}

这工作正常,但访问令牌最终会过期。此时操作开始失败。理论上有一个refresh token可以用来更新access token,但是好像不是IAuthenticationResult接口的一部分。我的问题是:

更新:回复很有帮助,但最终我无法在 clientId/tenantId 被使用时获得真正的令牌更新。这可能是设计使然?无论如何,只需在到期前获取新令牌即可,请参阅以下代码段:

IAuthenticationResult authResult = generateOAuthToken();
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(account_name, authResult.accessToken());
new Thread(new OAuthTokenRenewer(authResult, storageCredentialsToken)).start();
storageAccount = new CloudStorageAccount(storageCredentialsToken, true);

private IAuthenticationResult generateOAuthToken() throws MalformedURLException {
  String authority = String.format(ACTIVE_DIRECTORY_ENDPOINT + "/%s/oauth2/v2.0/token", tenant_id);
  Set<String> scope = Collections.singleton(String.format("https://%s.blob.core.windows.net/.default", account_name));
  IClientCredential credential = ClientCredentialFactory.createFromSecret(client_secret);
  ConfidentialClientApplication cca = ConfidentialClientApplication
      .builder(client_id, credential)
      .authority(authority)
      .build();
  ClientCredentialParameters parameters = ClientCredentialParameters
      .builder(scope)
      .build();
  return cca.acquireToken(parameters).join();
}


private class OAuthTokenRenewer implements Runnable {
  IAuthenticationResult authResult;
  final StorageCredentialsToken storageCredentialsToken;
  public OAuthTokenRenewer(IAuthenticationResult authResult, StorageCredentialsToken storageCredentialsToken) {
    this.authResult = authResult;
    this.storageCredentialsToken = storageCredentialsToken;
  }
  @Override
  public void run() {
    while (true) {
      try {
        Thread.sleep(60 * 1000L);
        long now = System.currentTimeMillis();
        if (authResult.expiresOnDate().getTime() - now < RENEWAL_WINDOW_MS) {
          authResult = generateOAuthToken();
          storageCredentialsToken.updateToken(authResult.accessToken());
        }
      } catch (Exception ex) {
      }
    }
  }
}

AuthenticationContext.acquireTokenSilent:

该函数将首先查看缓存并自动检查令牌是否过期。此外,如果在缓存中找不到合适的访问令牌,但刷新令牌可用,该函数将自动使用刷新令牌。此方法不会为用户显示 UI。如果需要提示,该方法将 return 异常

如果使用MSAL获取token,refresh token会存储在不暴露的缓存中。我们可以使用 acquireTokenSilently

获取新的访问令牌
SilentParameters parameters = SilentParameters.builder(
        Collections.singleton("User.ReadBasic.All"),
        result.account()).build();

CompletableFuture<IAuthenticationResult> future = app.acquireTokenSilently(parameters);
IAuthenticationResult updatedResult = future.get();

参考:SO Thread