【Azure Developer】使用 Microsoft Authentication Libraries (MSAL) 如何来获取Token呢 (通过用户名和密码方式获取Access Token)
阅读原文时间:2022年05月08日阅读:1

问题描述

在上一篇博文《【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢 (通过用户名和密码方式获取Access Token)》中,介绍了使用ADAL4J SDK获取Access Token。而ADAL4J是非常旧的SDK,最新的SDK名称为 MSAL4J (Microsoft Authentication Libraries),原来的AcquireToken的函数与现在的方式变动较大,不能直接修改POM.XML中依赖的方式来解决问题。

/\*\*  
 \* 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);  
}

public CompletableFuture<IAuthenticationResult> acquireToken(UserNamePasswordParameters parameters) {

    validateNotNull("parameters", parameters);

    UserNamePasswordRequest userNamePasswordRequest =  
            new UserNamePasswordRequest(parameters,  
                    this,  
                    createRequestContext(PublicApi.ACQUIRE\_TOKEN\_BY\_USERNAME\_PASSWORD));

    return this.executeRequest(userNamePasswordRequest);  
}

/\*\*  
 \* Builder for UserNameParameters

 \* @param scopes scopes application is requesting access to

 \* @param username username of the account

 \* @param password char array containing credentials for the username

 \* @return builder object that can be used to construct UserNameParameters  
 \*/  
public static UserNamePasswordParametersBuilder builder(Set<String> scopes, String username, char\[\] password) {  
    validateNotEmpty("scopes", scopes);  
    validateNotBlank("username", username);  
    validateNotEmpty("password", password);  
    return builder().scopes(scopes).username(username).password(password);  
}

那么,通过MSAL4J SDK,如何使用用户名,密码来获取到Access Token呢?

问题解答

和使用ADAL4J一样,都是需要使用Azure AD中的用户,以及一个Azure AD 注册应用(此应用需要开启“Allow public client flows”功能),开启步骤见博文《【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢 (通过用户名和密码方式获取Access Token)》中。

package com.example;

import java.util.Collections;
import java.util.Set;
import com.microsoft.aad.msal4j.*;

/**
* Hello world!
*
*/
public class App {
private static String authority = "https://login.chinacloudapi.cn/ tenant id>/";
private static Set scope = Collections.singleton("https://ossrdbms-aad.database.chinacloudapi.cn/.default");
private static String clientId ="Azure AD Application(Client) ID";
private static String username ="AAD USER
@XXXX.partner.onmschina.cn";
private static String password = "USER PASSWORD";

public static void main(String\[\] args) throws Exception {  
    System.out.println("Hello World!");

    System.out.println("Hello App to get Token by Username & Password....");

    PublicClientApplication pca = PublicClientApplication.builder(clientId)  
            .authority(authority)  
            .build();

    //Get list of accounts from the application's token cache, and search them for the configured username  
    //getAccounts() will be empty on this first call, as accounts are added to the cache when acquiring a token  
    Set<IAccount> accountsInCache = pca.getAccounts().join();  
    IAccount account = getAccountByUsername(accountsInCache, username);

    //Attempt to acquire token when user's account is not in the application's token cache  
    IAuthenticationResult result = acquireTokenUsernamePassword(pca, scope, account, username, password);  
    System.out.println("Account username: " + result.account().username());  
    System.out.println("Access token:     " + result.accessToken());  
    System.out.println("Id token:         " + result.idToken());  
    System.out.println();

    accountsInCache = pca.getAccounts().join();  
    account = getAccountByUsername(accountsInCache, username);

    //Attempt to acquire token again, now that the user's account and a token are in the application's token cache  
    result = acquireTokenUsernamePassword(pca, scope, account, username, password);  
    System.out.println("Account username: " + result.account().username());  
    System.out.println("Access token:     " + result.accessToken());  
    System.out.println("Id token:         " + result.idToken());

}

private static IAuthenticationResult acquireTokenUsernamePassword(PublicClientApplication pca,  
                                                                  Set<String> scope,  
                                                                  IAccount account,  
                                                                  String username,  
                                                                  String password) throws Exception {  
    IAuthenticationResult result;  
    try {  
        SilentParameters silentParameters =  
                SilentParameters  
                        .builder(scope)  
                        .account(account)  
                        .build();  
        // Try to acquire token silently. This will fail on the first acquireTokenUsernamePassword() call  
        // because the token cache does not have any data for the user you are trying to acquire a token for  
        result = pca.acquireTokenSilently(silentParameters).join();  
        System.out.println("==acquireTokenSilently call succeeded");  
    } catch (Exception ex) {  
        if (ex.getCause() instanceof MsalException) {  
            System.out.println("==acquireTokenSilently call failed: " + ex.getCause());  
            UserNamePasswordParameters parameters =  
                    UserNamePasswordParameters  
                            .builder(scope, username, password.toCharArray())  
                            .build();  
            // Try to acquire a token via username/password. If successful, you should see  
            // the token and account information printed out to console  
            result = pca.acquireToken(parameters).join();  
            System.out.println("==username/password flow succeeded");  
        } else {  
            // Handle other exceptions accordingly  
            throw ex;  
        }  
    }  
    return result;  
}

    /\*\*  
 \* Helper function to return an account from a given set of accounts based on the given username,  
 \* or return null if no accounts in the set match  
 \*/  
private static IAccount getAccountByUsername(Set<IAccount> accounts, String username) {  
    if (accounts.isEmpty()) {  
        System.out.println("==No accounts in cache");  
    } else {  
        System.out.println("==Accounts in cache: " + accounts.size());  
        for (IAccount account : accounts) {  
            if (account.username().equals(username)) {  
                return account;  
            }  
        }  
    }  
    return null;  
}

}

在POM.XML文件中添加依赖Package:

<dependency>  
  <groupId>com.microsoft.azure</groupId>  
  <artifactId>msal4j</artifactId>  
  <version>1.0.0</version>  

注意:以上代码最关键的部分就是 UserNamePasswordParameters 的设置。scope 也是需要根据Token的资源而变动,如以上示例代码中使用的 https://ossrdbms-aad.database.chinacloudapi.cn/.default , 而在adal4j的示例中,resource的值为:https://microsoftgraph.chinacloudapi.cn/。

错误消息:

Caused by: com.microsoft.aad.adal4j.AuthenticationException:
{"error_description":
"AADSTS65001: The user or administrator has not consented to use the application with ID 'xxxxxxxx-xxxx-4fa8-xxxx-xxxxxxxxxxxx' named 'xxxxtest01'.
Send an interactive authorization request for this user and resource.\r\n
Trace ID:xxxxxx-xxx-xxx----xxxxxx\r\n
Correlation ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n
Timestamp: 2022-05-05 08:16:16Z",
"error":"invalid_grant"}

此类问题的解决方法为:

1)进入Azure AD页面,找到当前User的登录日志信息(Sign-in logs),查看失败的记录,在详细记录中,查看Status为 Interrupted的记录,找到 Resource 和Application 信息。在第二步中使用这两个信息。

2)回到Azure AD的注册应用页面,找到第一步中的Applicaiton,然后进入API Permission页面。在API Permission页面中点击“Add a Permission”,然后再“APIs my Organization uses”的文本框中输入“Azure OSSRDBMS Database”进行搜索,然后选中它,并赋予“Delegated  Permissions”权限。如下图:

参考资料

Java console application letting users sign-in with username/password and call Microsoft Graph API:https://github.com/Azure-Samples/ms-identity-java-desktop/tree/da27a1af6064d5e833e645e5040a5120a0c2698f/Username-Password-Flow

Microsoft identity platform and OAuth 2.0 Resource Owner Password Credentials:https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc

使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢 (通过用户名和密码方式获取Access Token) : https://www.cnblogs.com/lulight/p/16212275.html

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章