問題描述
使用中國區的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 有以下四個值。
- AzureChina :The host of the Azure Active Directory authority for tenants in the Azure China Cloud. AZURE_CHINA = "login.chinacloudapi.cn"
- AzureGermany: The host of the Azure Active Directory authority for tenants in the Azure German Cloud. AZURE_GERMANY = "login.microsoftonline.de"
- AzureGovernment: The host of the Azure Active Directory authority for tenants in the Azure US Government Cloud. AZURE_GOVERNMENT = "login.microsoftonline.us"
- 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)
第二個問題:acquireTokne方法中的 https://graph.windows.net 是指向global azure的資源,如果是中國區azure的資源,那麼resource url是多少呢?
根據中國區Azure的開發文件,並沒有查詢到對應於 graph.windows.net的中國區Graph 終結點。但是,中國區Graph 的終結點為:microsoftgraph.chinacloudapi.cn,所以,以上示例中應該使用的值應是:
第三個問題: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呢?
- 進入AAD, 選擇註冊應用( App Registrations:https://portal.azure.cn/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps)
- 並在Onwed Applications 中選擇,進入詳細頁面或就是當前頁面,獲取Application(Client) ID
特別注意:這個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”。
完成參考例項程式碼
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的使用者名稱和密碼)
測試結果:
獲取Token成功。
可以通過一個公用網站 jwt.io 來解析Token: https://jwt.io/, 它可以解析出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