从移动应用调用 Web API
在你的应用将用户登录并收到令牌以后,Microsoft 身份验证库 (MSAL) 会公开有关用户、用户的环境以及所颁发令牌的信息。 应用可以使用这些值来调用 web API 或向用户显示欢迎消息。
本文首先介绍 MSAL 结果。 然后介绍如何使用 AuthenticationResult
或 result
中的访问令牌来调用受保护的 Web API。
MSAL 结果
MSAL 提供下列值:
AccessToken
在 HTTP 持有者请求中调用受保护的 Web API。IdToken
包含有关已登录用户的有用信息。 此信息包括用户的姓名、主租户和存储的唯一标识符。ExpiresOn
是令牌的过期时间。 MSAL 处理应用的自动刷新。TenantId
是用户登录到的租户的标识符。 对于 Microsoft Entra B2B 中的来宾用户,此值标识用户登录到的租户。 该值不能标识用户的主租户。Scopes
指示通过令牌授予的作用域。 授予的作用域可能是你请求的作用域的子集。
MSAL 还为 Account
值提供抽象。 Account
值表示当前用户的登录帐户:
HomeAccountIdentifier
标识用户的主租户。UserName
是用户的首选用户名。 对于 Azure AD B2C 用户,此值可能为空。AccountIdentifier
标识已登录的用户。 在大多数情况下,此值与HomeAccountIdentifier
值相同,除非用户是另一个租户中的来宾。
调用 API
拥有访问令牌后,可以调用 Web API。 应用将使用该令牌生成 HTTP 请求,然后运行该请求。
Android
RequestQueue queue = Volley.newRequestQueue(this);
JSONObject parameters = new JSONObject();
try {
parameters.put("key", "value");
} catch (Exception e) {
// Error when constructing.
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
parameters,new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
// Successfully called Graph. Process data and send to UI.
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// Error.
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
// Put access token in HTTP request.
headers.put("Authorization", "Bearer " + authResult.getAccessToken());
return headers;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(
3000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
适用于 iOS 和 MacOS 的 MSAL
用于获取令牌的方法将返回 MSALResult
的对象。 MSALResult
公开 accessToken
属性。 可以使用 accessToken
来调用 Web API。 在调用之前,将此属性添加到 HTTP 授权标头,用于访问受保护的 Web API。
NSMutableURLRequest *urlRequest = [NSMutableURLRequest new];
urlRequest.URL = [NSURL URLWithString:"https://contoso.api.com"];
urlRequest.HTTPMethod = @"GET";
urlRequest.allHTTPHeaderFields = @{ @"Authorization" : [NSString stringWithFormat:@"Bearer %@", accessToken] };
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:urlRequest
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];
[task resume];
let urlRequest = NSMutableURLRequest()
urlRequest.url = URL(string: "https://contoso.api.com")!
urlRequest.httpMethod = "GET"
urlRequest.allHTTPHeaderFields = [ "Authorization" : "Bearer \(accessToken)" ]
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data: Data?, response: URLResponse?, error: Error?) in }
task.resume()
Xamarin
MSAL.NET 中的 AuthenticationResult 属性
用于获取令牌的方法返回 AuthenticationResult
。 对于异步方法,将返回 Task<AuthenticationResult>
。
在 MSAL.NET 中,AuthenticationResult
会公开
AccessToken
,以便 Web API 访问资源。 此参数是一个字符串,通常是一个 Base-64 编码的 JWT。 客户端不应该查看访问令牌的内部。 不保证格式稳定,并且可以为资源加密令牌。 编写的代码依赖于客户端上的访问令牌内容是最大的错误来源之一,并且会违反客户端逻辑。 有关详细信息,请参阅访问令牌。- 用户的
IdToken
。 此参数是编码的 JWT。 有关详细信息,请参阅 ID 令牌。 ExpiresOn
会告知令牌过期的日期和时间。TenantId
包含用户所在的租户。 对于来宾用户(Microsoft Entra B2B 方案),租户 ID 是来宾租户,而不是唯一的租户。 为用户传送令牌时,AuthenticationResult
还包含有关此用户的信息。 对于在请求令牌时未提供应用用户的机密客户端流,此用户信息为 null。- 令牌的颁发
Scopes
。 - 用户的唯一 ID。
IAccount
MSAL.NET 通过 IAccount
接口定义了帐户的概念。 此中断性变更提供了正确的语义。 同一用户可以在不同的 Microsoft Entra 目录中拥有多个帐户。 此外,由于会提供主帐户信息,MSAL.NET 可以在使用来宾方案的情况下提供更有用的信息。
下图显示了 IAccount
接口的结构。
AccountId
类使用下表中显示的属性标识特定租户中的帐户。
属性 | 说明 |
---|---|
TenantId |
GUID 的字符串表示形式,是帐户所在租户的 ID。 |
ObjectId |
GUID 的字符串表示形式,是拥有租户中的帐户的用户的 ID。 |
Identifier |
帐户的唯一标识符。 Identifier 是 ObjectId 和 TenantId 的串联,由逗号分隔。 它们不是 Base 64 编码的。 |
IAccount
接口表示单个帐户的相关信息。 同一用户可以存在于不同的租户中,这意味着一个用户可以有多个帐户。 其成员显示在下表中。
属性 | 说明 |
---|---|
Username |
一个字符串,包含 UserPrincipalName (UPN) 格式的可显示值,例如 john.doe@contoso.com。 此字符串可以为 null,这不同于 HomeAccountId 和 HomeAccountId.Identifier,后两者不会为 null。 此属性替换 MSAL.NET 旧版本中 IUser 的 DisplayableId 属性。 |
Environment |
一个字符串,包含此帐户的标识提供者,例如 login.partner.microsoftonline.cn 。 此属性替换 IUser 的 IdentityProvider 属性,不同之处是 IdentityProvider 还包含除云环境以外的租户信息。 而此处的该值仅仅是主机。 |
HomeAccountId |
用户的主帐户的帐户 ID。 此属性在 Microsoft Entra 租户中唯一标识用户。 |
使用令牌调用受保护的 API
在 MSAL 在 result
中返回 AuthenticationResult
后,将它添加到 HTTP 授权标头,然后再调用该令牌以访问受保护的 Web API。
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Call the web API.
HttpResponseMessage response = await _httpClient.GetAsync(apiUri);
...
}
发出多个 API 请求
若要多次调用同一 API,或者要调用多个 API,请在构建应用时考虑以下主题:
增量同意:Microsoft 标识平台允许应用在需要权限时(而不是在开始时)获取用户同意。 每次应用准备好调用 API 时,它应只请求所需的作用域。
条件访问:当发出多个 API 请求时,在某些情况下可能必须满足其他条件访问要求。 如果第一个请求没有条件访问策略,并且你的应用尝试以无提示方式访问要求条件访问的新 API,这样就会导致所需满足的要求增加。 若要解决此问题,请务必捕获无提示请求中的错误,并准备好发送交互式请求。 有关详细信息,请参阅条件访问指导。
使用增量同意和条件访问调用多个 API
若要为同一用户调用多个 API,则在为用户获取令牌后,可以通过随后调用 AcquireTokenSilent
获取令牌来避免重复要求用户提供凭据:
var result = await app.AcquireTokenXX("scopeApi1")
.ExecuteAsync();
result = await app.AcquireTokenSilent("scopeApi2")
.ExecuteAsync();
以下情况需要交互:
- 用户同意了第一个 API,但现在需要同意更多作用域。 在这种情况下,使用增量同意。
- 第一个 API 不需要多重身份验证,但下一个 API 需要。
var result = await app.AcquireTokenXX("scopeApi1")
.ExecuteAsync();
try
{
result = await app.AcquireTokenSilent("scopeApi2")
.ExecuteAsync();
}
catch(MsalUiRequiredException ex)
{
result = await app.AcquireTokenInteractive("scopeApi2")
.WithClaims(ex.Claims)
.ExecuteAsync();
}
后续步骤
转到此方案中的下一篇文章:移到生产环境。