当前位置 : 主页 > 编程语言 > 其它开发 >

【Azure Developer】使用 Microsoft Authentication Libraries (MSAL) 如何来获取Token呢 (通过用

来源:互联网 收集:自由互联 发布时间:2022-05-22
问题描述 在上一篇博文《【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢 (通过用户名和密码方式获取Access Token)》中,介绍了使用ADAL4J SDK获取Ac
问题描述

在上一篇博文《【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中依赖的方式来解决问题。

ADAL4J的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);
    }
MSAL4J的acquireToken方法:
    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/<your tenant id>/";
    private static Set<String> 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>
  </dependency>

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

运行效果为

 

附录一:遇见 Administrator has not consented the application的问题 错误消息:
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

 

当在复杂的环境中面临问题,格物之道需:浊而静之徐清,安以动之徐生。 云中,恰是如此!

网友评论