了解如何为调用 Web API 的守护程序应用程序配置代码。
支持守护程序应用的 Microsoft 库
以下 Microsoft 库支持守护程序应用:
1 联机服务通用许可条款适用于公共预览版中的库。
守护程序应用程序使用应用程序权限,而不是委托的权限。 因此,它们支持的帐户类型不能是任何组织目录中的帐户。 你需要选择“我的组织中的帐户”或“任何组织中的帐户”。
在应用程序配置中指定的颁发机构应该是租户的(指定租户 ID 或者与组织相关联的域名)。
即使在需要提供多租户工具的情况下,也应在此流中使用租户 ID 或域名,而不是 common
或 organizations
,因为该服务无法可靠推断应使用哪个租户。
在 MSAL 库中,客户端凭据(机密或证书)是作为机密客户端应用程序构造的参数传递的。
重要
即使应用程序是作为服务运行的控制台应用程序,如果它是守护程序应用程序,则也需要是机密客户端应用程序。
配置文件
配置文件定义:
- 云实例和租户 ID,它们共同构成了“机构”。
- 通过应用程序注册获得的客户端 ID。
- 客户端机密或证书。
下面是关于在 appsettings.json 文件中定义配置的示例。 此示例摘自 GitHub 上的 .NET 控制台守护程序代码示例。
{
"AzureAd": {
"Instance": "https://login.partner.microsoftonline.cn/",
"TenantId": "[Enter here the tenantID or domain name for your Azure AD tenant]",
"ClientId": "[Enter here the ClientId for your application]",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "[Enter here a client secret for your application]"
}
]
}
}
请提供证书而不是客户端密码或工作负载联合身份验证凭据。
private final static String CLIENT_ID = "";
private final static String AUTHORITY = "https://login.partner.microsoftonline.cn/<tenant>/";
private final static String CLIENT_SECRET = "";
private final static Set<String> SCOPE = Collections.singleton("https://microsoftgraph.chinacloudapi.cn/.default");
Node.js 守护程序示例的配置参数位于 .env 文件中:
# Credentials
TENANT_ID=Enter_the_Tenant_Info_Here
CLIENT_ID=Enter_the_Application_Id_Here
// You provide either a ClientSecret or a CertificateConfiguration, or a ClientAssertion. These settings are exclusive
CLIENT_SECRET=Enter_the_Client_Secret_Here
CERTIFICATE_THUMBPRINT=Enter_the_certificate_thumbprint_Here
CERTIFICATE_PRIVATE_KEY=Enter_the_certificate_private_key_Here
CLIENT_ASSERTION=Enter_the_Assertion_String_Here
# Endpoints
// the Azure AD endpoint is the authority endpoint for token issuance
AAD_ENDPOINT=Enter_the_Cloud_Instance_Id_Here // https://login.partner.microsoftonline.cn/
// the graph endpoint is the application ID URI of Microsoft Graph
GRAPH_ENDPOINT=Enter_the_Graph_Endpoint_Here // https://microsoftgraph.chinacloudapi.cn/
使用客户端机密构建机密客户端时,Python 守护程序示例中的 parameters.json 配置文件如下所示:
{
"authority": "https://login.partner.microsoftonline.cn/<your_tenant_id>",
"client_id": "your_client_id",
"scope": [ "https://microsoftgraph.chinacloudapi.cn/.default" ],
"secret": "The secret generated by Azure AD during your confidential app registration",
"endpoint": "https://microsoftgraph.chinacloudapi.cn/v1.0/users"
}
使用证书构建机密客户端时,Python 守护程序示例中的 parameters.json 配置文件如下所示:
{
"authority": "https://login.partner.microsoftonline.cn/<your_tenant_id>",
"client_id": "your_client_id",
"scope": [ "https://microsoftgraph.chinacloudapi.cn/.default" ],
"thumbprint": "790E... The thumbprint generated by Azure AD when you upload your public cert",
"private_key_file": "server.pem",
"endpoint": "https://microsoftgraph.chinacloudapi.cn/v1.0/users"
}
下面是关于在 appsettings.json 文件中定义配置的示例。 此示例摘自 GitHub 上的 .NET 控制台守护程序代码示例。
{
"Instance": "https://login.partner.microsoftonline.cn/{0}",
"Tenant": "[Enter here the tenantID or domain name for your Azure AD tenant]",
"ClientId": "[Enter here the ClientId for your application]",
"ClientSecret": "[Enter here a client secret for your application]",
"CertificateName": "[Or instead of client secret: Enter here the name of a certificate (from the user cert store) as registered with your application]"
}
请提供 ClientSecret
或 CertificateName
。 这些设置是互斥的。
实例化 MSAL 应用程序
若要实例化 MSAL 应用程序,请添加、引用或导入 MSAL 包(取决于语言)。
构造取决于你是使用客户端机密还是使用证书(还是使用已签名断言,这是一种高级方案)。
引用此包
在应用程序代码中引用 MSAL 包。
向应用程序添加 Microsoft.Identity.Web.TokenAcquisition NuGet 包。
或者,如果要调用 Microsoft Graph,请添加 Microsoft.Identity.Web.GraphServiceClient 包。
你的项目可能如下所示。 需要将 appsettings.json 文件复制到输出目录。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>daemon_console</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="2.12.2" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
在 Program.cs 文件中,在代码中添加 using
指令以引用 Microsoft.Identity.Web。
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
import com.microsoft.aad.msal4j.ClientCredentialFactory;
import com.microsoft.aad.msal4j.ClientCredentialParameters;
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
import com.microsoft.aad.msal4j.IClientCredential;
import com.microsoft.aad.msal4j.MsalException;
import com.microsoft.aad.msal4j.SilentParameters;
通过运行 package.json
文件所在的文件夹中的 npm install
安装这些包。 然后,导入 msal-node
包:
const msal = require('@azure/msal-node');
import msal
import json
import sys
import logging
将 Microsoft.Identity.Client NuGet 包添加到应用程序,然后在代码中添加一个 using
指令以引用它。
在 MSAL.NET 中,机密客户端应用程序通过 IConfidentialClientApplication
接口表示。
using Microsoft.Identity.Client;
IConfidentialClientApplication app;
使用客户端机密实例化机密客户端应用程序
下面的代码用于使用客户端机密实例化机密客户端应用程序:
class Program
{
static async Task Main(string[] _)
{
// Get the Token acquirer factory instance. By default it reads an appsettings.json
// file if it exists in the same folder as the app (make sure that the
// "Copy to Output Directory" property of the appsettings.json file is "Copy if newer").
TokenAcquirerFactory tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
// Configure the application options to be read from the configuration
// and add the services you need (Graph, token cache)
IServiceCollection services = tokenAcquirerFactory.Services;
services.AddMicrosoftGraph();
// By default, you get an in-memory token cache.
// For more token cache serialization options, see https://aka.ms/msal-net-token-cache-serialization
// Resolve the dependency injection.
var serviceProvider = tokenAcquirerFactory.Build();
// ...
}
}
从 appsettings.json 读取配置:
IClientCredential credential = ClientCredentialFactory.createFromSecret(CLIENT_SECRET);
ConfidentialClientApplication cca =
ConfidentialClientApplication
.builder(CLIENT_ID, credential)
.authority(AUTHORITY)
.build();
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID,
authority: process.env.AAD_ENDPOINT + process.env.TENANT_ID,
clientSecret: process.env.CLIENT_SECRET,
}
};
const apiConfig = {
uri: process.env.GRAPH_ENDPOINT + 'v1.0/users',
};
const tokenRequest = {
scopes: [process.env.GRAPH_ENDPOINT + '.default'],
};
const cca = new msal.ConfidentialClientApplication(msalConfig);
# Pass the parameters.json file as an argument to this Python script. E.g.: python your_py_file.py parameters.json
config = json.load(open(sys.argv[1]))
# Create a preferably long-lived app instance that maintains a token cache.
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential=config["secret"],
# token_cache=... # Default cache is in memory only.
# You can learn how to use SerializableTokenCache from
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
)
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority))
.Build();
Authority
是云实例和租户 ID 的串联,例如 https://login.partner.microsoftonline.cn/contoso.partner.onmschina.cn
或 https://login.partner.microsoftonline.cn/aaaabbbb-0000-cccc-1111-dddd2222eeee
。 在配置文件部分中显示的 appsettings.json 文件中,实例和租户分别由 Instance
和 Tenant
值表示。
在上一片段源自的代码示例中,Authority
是 AuthenticationConfig 类上的一个属性,其定义如下:
/// <summary>
/// URL of the authority
/// </summary>
public string Authority
{
get
{
return String.Format(CultureInfo.InvariantCulture, Instance, Tenant);
}
}
通过客户端证书实例化机密客户端应用程序
下面的代码用于使用证书来构建应用程序:
代码本身完全一样。 配置中介绍了证书。
获取证书有多种方式。 有关详细信息,请参阅 https://aka.ms/ms-id-web-certificates。
下面介绍了如何从 KeyVault 获取证书。 Microsoft 标识委托给 Azure 标识的 DefaultAzureCredential,并使用了托管标识(如果可用)从 KeyVault 访问证书。 可在本地调试应用程序,因为它届时会使用开发人员凭据。
"ClientCredentials": [
{
"SourceType": "KeyVault",
"KeyVaultUrl": "https://yourKeyVaultUrl.vault.azure.cn",
"KeyVaultCertificateName": "NameOfYourCertificate"
}
在 MSAL Java 中,可以通过两个生成器使用证书来实例化机密客户端应用程序:
InputStream pkcs12Certificate = ... ; /* Containing PCKS12-formatted certificate*/
string certificatePassword = ... ; /* Contains the password to access the certificate */
IClientCredential credential = ClientCredentialFactory.createFromCertificate(pkcs12Certificate, certificatePassword);
ConfidentialClientApplication cca =
ConfidentialClientApplication
.builder(CLIENT_ID, credential)
.authority(AUTHORITY)
.build();
或
PrivateKey key = getPrivateKey(); /* RSA private key to sign the assertion */
X509Certificate publicCertificate = getPublicCertificate(); /* x509 public certificate used as a thumbprint */
IClientCredential credential = ClientCredentialFactory.createFromCertificate(key, publicCertificate);
ConfidentialClientApplication cca =
ConfidentialClientApplication
.builder(CLIENT_ID, credential)
.authority(AUTHORITY)
.build();
const config = {
auth: {
clientId: process.env.CLIENT_ID,
authority: process.env.AAD_ENDPOINT + process.env.TENANT_ID,
clientCertificate: {
thumbprint: process.env.CERTIFICATE_THUMBPRINT, // a 40-digit hexadecimal string
privateKey: process.env.CERTIFICATE_PRIVATE_KEY,
}
}
};
// Create an MSAL application object
const cca = new msal.ConfidentialClientApplication(config);
有关详细信息,请参阅将证书凭据用于 MSAL 节点。
# Pass the parameters.json file as an argument to this Python script. E.g.: python your_py_file.py parameters.json
config = json.load(open(sys.argv[1]))
# Create a preferably long-lived app instance that maintains a token cache.
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()},
# token_cache=... # Default cache is in memory only.
# You can learn how to use SerializableTokenCache from
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
)
X509Certificate2 certificate = ReadCertificate(config.CertificateName);
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithCertificate(certificate)
.WithAuthority(new Uri(config.Authority))
.Build();
高级方案:使用客户端断言实例化机密客户端应用程序
IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(assertion);
ConfidentialClientApplication cca =
ConfidentialClientApplication
.builder(CLIENT_ID, credential)
.authority(AUTHORITY)
.build();
const clientConfig = {
auth: {
clientId: process.env.CLIENT_ID,
authority: process.env.AAD_ENDPOINT + process.env.TENANT_ID,
clientAssertion: process.env.CLIENT_ASSERTION
}
};
const cca = new msal.ConfidentialClientApplication(clientConfig);
有关详细信息,请参阅初始化 ConfidentialClientApplication 对象。
在 MSAL Python 中,可以使用将要由此 ConfidentialClientApplication
的私钥签名的声明来提供客户端声明。
# Pass the parameters.json file as an argument to this Python script. E.g.: python your_py_file.py parameters.json
config = json.load(open(sys.argv[1]))
# Create a preferably long-lived app instance that maintains a token cache.
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential={"thumbprint": config["thumbprint"], "private_key": open(config['private_key_file']).read()},
client_claims = {"client_ip": "x.x.x.x"}
# token_cache=... # Default cache is in memory only.
# You can learn how to use SerializableTokenCache from
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
)
如需详细信息,请参阅 MSAL Python 的 ConfidentialClientApplication 参考文档。
机密客户端应用程序还可以使用客户端断言(而不是客户端密码或证书)来证明其身份。
MSAL.NET 可以通过两种方法将签名的断言提供给机密客户端应用:
.WithClientAssertion()
.WithClientClaims()
使用 WithClientAssertion
时,请提供签名的 JWT。 客户端断言详细介绍了这一高级方案。
string signedClientAssertion = ComputeAssertion();
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientAssertion(signedClientAssertion)
.Build();
使用 WithClientClaims
时,MSAL.NET 会生成一个已签名断言,其中包含 Microsoft Entra ID 预期的声明,以及你想要发送的其他客户端声明。
此代码展示了如何执行该操作:
string ipAddress = "192.168.1.2";
var claims = new Dictionary<string, string> { { "client_ip", ipAddress } };
X509Certificate2 certificate = ReadCertificate(config.CertificateName);
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithAuthority(new Uri(config.Authority))
.WithClientClaims(certificate, claims)
.Build();
同样,如需详细信息,请参阅客户端断言。
后续步骤