【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

路邊兩盞燈發表於2022-05-01

問題描述

使用中國區的Azure,在獲取Token時候,參考了 adal4j的程式碼,在官方文件中,發現瞭如下的片段程式碼:

ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
    "https://graph.windows.net", YOUR_TENANT_ID, username, password,
    null);
AuthenticationResult result = future.get();
System.out.println("Access Token - " + result.getAccessToken());
System.out.println("Refresh Token - " + result.getRefreshToken());
System.out.println("ID Token - " + result.getIdToken());

以上程式碼中,有一些引數很不明確:

1)AUTHORITY, 是什麼意思呢?

2)acquireTokne方法中的 https://graph.windows.net 是指向global azure的資源,如果是中國區azure的資源,那麼resource url是多少呢?

3)YOUR_TENANT_ID,它的值是什麼呢?

 

問題解答

第一個問題:AUTHORITY, 是什麼意思,它的值是什麼呢?

AUTHORITY,表示認證的主體,它是一個URL,表示可以從該主體中獲取到認證Token。 它的格式為:https://<authority host>/<tenant id> ,所以在使用Azure的過程中,根據Azure環境的不同,Host 有以下四個值。

  1. AzureChina :The host of the Azure Active Directory authority for tenants in the Azure China Cloud.  AZURE_CHINA "login.chinacloudapi.cn"
  2. AzureGermany: The host of the Azure Active Directory authority for tenants in the Azure German Cloud.  AZURE_GERMANY "login.microsoftonline.de"
  3. AzureGovernment: The host of the Azure Active Directory authority for tenants in the Azure US Government Cloud.  AZURE_GOVERNMENT "login.microsoftonline.us"
  4. AzurePublicCloud: The host of the Azure Active Directory authority for tenants in the Azure Public Cloud. AZURE_PUBLIC_CLOUD "login.microsoftonline.com"

所以,這裡我們需要使用的值為:String AUTHORITY = "https://login.chinacloudapi.cn/<tenant id >";  

那麼如何來獲取Tenant ID呢? 

登入到Azure門戶 --> 進入AAD中,在Overview頁面檢視Tenant ID (https://portal.azure.cn/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview)

【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

 

第二個問題:acquireTokne方法中的 https://graph.windows.net 是指向global azure的資源,如果是中國區azure的資源,那麼resource url是多少呢?

根據中國區Azure的開發文件,並沒有查詢到對應於 graph.windows.net的中國區Graph 終結點。但是,中國區Graph 的終結點為:microsoftgraph.chinacloudapi.cn,所以,以上示例中應該使用的值應是:

https://microsoftgraph.chinacloudapi.cn/
【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

 

第三個問題:YOUR_TENANT_ID,它的值是什麼呢?

在對比了adal4j的原始碼後,在acquireToken方法定義中,發現YOUR_TENANT_ID所對應的值應該是 clientId ()。所以,官網參考文件中的YOUR_TENANT_ID存在誤導情景。需要修改為YOUR_CLIENT_ID。

ADAL4J中acquireToken原始碼(acquireToken有多個過載,但此處只列舉出程式碼中使用的這個過載)

    /**
     * Acquires a security token from the authority using a Refresh Token
     * previously received.
     *
     * @param clientId
     *            Name or ID of the client requesting the token.
     * @param resource
     *            Identifier of the target resource that is the recipient of the
     *            requested token. If null, token is requested for the same
     *            resource refresh token was originally issued for. If passed,
     *            resource should match the original resource used to acquire
     *            refresh token unless token service supports refresh token for
     *            multiple resources.
     * @param username
     *            Username of the managed or federated user.
     * @param password
     *            Password of the managed or federated user.
     * @param callback
     *            optional callback object for non-blocking execution.
     * @return A {@link Future} object representing the
     *         {@link AuthenticationResult} of the call. It contains Access
     *         Token, Refresh Token and the Access Token's expiration time.
     */
    public Future<AuthenticationResult> acquireToken(final String resource,
            final String clientId, final String username,
            final String password, final AuthenticationCallback callback) {
        if (StringHelper.isBlank(resource)) {
            throw new IllegalArgumentException("resource is null or empty");
        }

        if (StringHelper.isBlank(clientId)) {
            throw new IllegalArgumentException("clientId is null or empty");
        }

        if (StringHelper.isBlank(username)) {
            throw new IllegalArgumentException("username is null or empty");
        }

        if (StringHelper.isBlank(password)) {
            throw new IllegalArgumentException("password is null or empty");
        }

        return this.acquireToken(new AdalAuthorizatonGrant(
                new ResourceOwnerPasswordCredentialsGrant(username, new Secret(
                        password)), resource), new ClientAuthenticationPost(
                ClientAuthenticationMethod.NONE, new ClientID(clientId)),
                callback);
    }

所以,這裡指定的Client ID 其實是,AAD中所註冊的一個應用(服務主體),而這個主體可以根據需求授予不同的許可權,acquireToken就是根據使用者驗證成功後,生成這個主題所擁有的許可權JWT令牌(Token),獲取到Token後,就擁有了訪問Azure中資源API的授權.

如何來獲取這個Client ID呢?

  • 並在Onwed Applications 中選擇,進入詳細頁面或就是當前頁面,獲取Application(Client) ID

【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

 

特別注意:這個App必須開啟 “Allow public client flows“ 才能成功獲取到 Token。 預設情況下,這裡選擇的是No。 如果不開啟這一步,將會收到錯誤訊息:"error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.

開啟方式為:點選這個App的名稱,進入詳細頁面,選擇Authentication,滑動到最底部,選擇“Allow public client flows”。

【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

完成參考例項程式碼

1:在POM.XML檔案中新增adal4j依賴

    <dependency>
      <groupId>com.microsoft.azure</groupId>
      <artifactId>adal4j</artifactId>
      <version>1.2.0</version>
  </dependency>

2:示例程式碼

package com.example;

import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;

/**
 * Hello world!
 *
 */
public class App {
    public static void main(String[] args) throws InterruptedException, ExecutionException, MalformedURLException {
        System.out.println("Hello World!");

        ExecutorService service = Executors.newFixedThreadPool(1);
        String AUTHORITY = "https://login.chinacloudapi.cn/<tenant id >"; // AzureAuthority 
   
        String YOUR_Client_ID="7b61c392-xxxx-xxxx-xxxx-xxxxxxxxxxx";
        String username = "xxxx@xxxx.xxx.onmschina.cn";
        String password = "xxxxxxxxxxx";
        AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
        Future<AuthenticationResult> future = context.acquireToken("https://microsoftgraph.chinacloudapi.cn/", YOUR_Client_ID,
                username, password, null);
        AuthenticationResult result = future.get();

        System.out.println("Access Token - " + result.getAccessToken());
        System.out.println("Refresh Token - " + result.getRefreshToken());
        System.out.println("ID Token - " + result.getIdToken());

    }
}

(PS: 使用的 username, password就是登入Azure的使用者名稱和密碼

測試結果:

【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

 

獲取Token成功。

可以通過一個公用網站 jwt.io 來解析Token: https://jwt.io/, 它可以解析出Token內容,讓我們可讀。

【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何來獲取Token呢

 

 

參考資料

Azure China developer guide:https://docs.microsoft.com/en-us/azure/china/resources-developer-guide#check-endpoints-in-azure

Azure Active Directory libraries for Java: https://docs.microsoft.com/en-us/java/api/overview/azure/activedirectory?view=azure-java-stable#client-library

 

相關文章