问题描述

在上一篇博文《【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

【Azure Developer】使用 Microsoft Authentication Libraries (MSAL) 如何来获取Token呢 (通过用户名和密码方式获取Access Token)的更多相关文章

  1. Azure AD, Endpoint Manger(Intune), SharePoint access token 的获取

    本章全是干货,干货,干货,重要的事情说三遍. 最近在研究Azure, Cloud相关的东西,项目中用的是Graph API(这个在下一章会相信介绍),可能是Graph API推出的时间比较晚,部分AP ...

  2. 【Azure Developer】使用 adal4j(Azure Active Directory authentication library for Java)如何来获取Token呢

    问题描述 使用中国区的Azure,在获取Token时候,参考了 adal4j的代码,在官方文档中,发现了如下的片段代码: ExecutorService service = Executors.new ...

  3. 【Azure Developer】使用Microsoft Graph API 批量创建用户,先后遇见的三个错误及解决办法

    问题描述 在先前的一篇博文中,介绍了如何使用Microsoft Graph API来创建Azure AD用户(博文参考:[Azure Developer]使用Microsoft Graph API 如 ...

  4. 【Azure Developer】Python 获取Micrisoft Graph API资源的Access Token, 并调用Microsoft Graph API servicePrincipals接口获取应用ID

    问题描述 在Azure开发中,我们时常面临获取Authorization问题,需要使用代码获取到Access Token后,在调用对应的API,如servicePrincipals接口. 如果是直接调 ...

  5. 【Azure Developer】解决Azure Key Vault管理Storage的示例代码在中国区Azure遇见的各种认证/授权问题 - C# Example Code

    问题描述 使用Azure密钥保管库(Key Vault)来托管存储账号(Storage Account)密钥的示例中,从Github中下载的示例代码在中国区Azure运行时候会遇见各种认证和授权问题, ...

  6. 【Azure Developer】【Python 】使用 azure.identity 和 azure.common.credentials 获取Azure AD的Access Token的两种方式

    问题描述 使用Python代码,展示如何从Azure AD 中获取目标资源的 Access Token. 如要了解如何从AAD中获取 client id,client secret,tenant id ...

  7. 【Azure 环境】【Azure Developer】使用Python代码获取Azure 中的资源的Metrics定义及数据

    问题描述 使用Python SDK来获取Azure上的各种资源的Metrics的名称以及Metrics Data的示例 问题解答 通过 azure-monitor-query ,可以创建一个 metr ...

  8. 【Azure Developer】使用Postman获取Azure AD中注册应用程序的授权Token,及为Azure REST API设置Authorization

    Azure Active Directory (Azure AD) is Microsoft's cloud-based identity and access management service, ...

  9. 【Azure Developer】在Azure Resource Graph Explorer中查看当前订阅下的所有资源信息列表并导出(如VM的名称,IP地址内网/公网,OS,区域等)

    问题描述 通过Azure的Resource Graph Explorer(https://portal.azure.cn/#blade/HubsExtension/ArgQueryBlade),可以查 ...

随机推荐

  1. 全面系统讲解CSS工作应用+面试一步搞定

    [TOC] 一.课程介绍 二.HTML基础强化 html常见元素和理解 html常见元素分类 head区元素:(不会在页面上留下元素) * meta * title * style * link * ...

  2. Codepen 每日精选(2018-4-11)

    按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以打开原始页面. 纯 css 写行走的大象https://codepen.io/FabioG/ful... 纯 css 画的 ...

  3. leetcode 1962. 移除石子使总数最小

    题目描述: 给你一个整数数组 piles ,数组 下标从 0 开始 ,其中 piles[i] 表示第 i 堆石子中的石子数量.另给你一个整数 k ,请你执行下述操作 恰好 k 次: 选出任一石子堆 p ...

  4. java中接口interface和private私有内部类怎样一块配合着用?

    3.接口interface和private内部类协同工作[新手可忽略不影响继续学习]马克-to-win:由于是private内部类,外面无法访问甚至无法看到你编的源代码(如果在不同的包中),非常安全. ...

  5. px,rem,em 通过媒体查询统一的代码

    @media only screen and (max-width: 1080px), only screen and (max-device-width:1080px) { html,body { ...

  6. 看完这篇文章你就可以告诉领导你精通Zookeeper了

    一.Zookeeper概述 1.概述 Zookeeper 是一个开源的为分布式框架提供协调服务的 Apache 项目.在分布式系统中,扮演注册中心的角色. Zookeeper数据模型的结构与Linux ...

  7. Spring Boot-@ImportResource注解

    @ImportResource:导入Spring的配置文件,让配置文件里面的内容生效 第一步:创建一个spring配置文件bean.xml <?xml version="1.0&quo ...

  8. linux添加磁盘及分区挂载

    磁盘管理 1.为什么要添加磁盘 随着系统的使用,磁盘的内容会越来越少,所以这时要添加磁盘增加空间 Linux系统中磁盘管理就是将硬盘通过挂载的方式挂载到linux文件系统中. 2.系统添加磁盘并分区 ...

  9. mybatis plus @TableId注解 type属性的含义

    首先该注解用在主键id上,它的type属性有8种类型 AUTO(0),NONE(1),INPUT(2),ASSIGN_ID(3),ASSIGN_UUID(4),ID_WORKER(3),ID_WORK ...

  10. CesiumJS 2022^ 原理[2] 渲染架构之三维物体 - 创建并执行指令

    目录 回顾 预备知识:指令 预备知识:通道 1. 生成并执行指令 1.1. Primitive 生成指令 1.2. Context 对象负责执行 WebGL 底层代码 2. 多段视锥体技术 3. 指令 ...