我也很难找到高质量的例子。以下是我如何解决从 Web assembly(托管或独立)应用程序调用 1 个或多个 API 的问题。
大多数 MSFT 示例仅处理一种 Api,因此在通过 AddMsalAuthentication 注册 Msal 时使用 options.ProviderOptions.DefaultAccessTokenScopes 选项。这会将您的令牌锁定为单一受众,当您有多个 api 需要调用时,这将不起作用。
相反,从 AuthorizationMessageHandler 类派生每个 api 端点的处理程序,设置authorizedUrland范围,为 DI 容器中的每个端点注册命名的 HttpClient,并使用 IHttpClientFactory 生成 HttpClient。
设想:
假设我有一个 WebAssembly 应用程序(托管或独立),它调用多个受保护的 api,包括 microsoft graph api。
首先,我必须为从 AuthorizationRequestMessageHandler 派生的每个 api 创建一个类:
Api 1:
// This message handler handles calls to the api at the endpoint "https://localhost:7040". It will generate tokens with the right audience and scope
// "aud": "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
// "scp": "access_as_user",
public class ApiOneAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
// ILogger if you want..
private readonly ILogger<ApiOneAuthorizationRequestMessageHandler> logger = default!;
public ApiOneAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager,
ILoggerFactory loggerFactory
)
: base(provider, navigationManager)
{
logger = loggerFactory.CreateLogger<ApiOneAuthorizationRequestMessageHandler>() ?? throw new ArgumentNullException(nameof(logger));
logger.LogDebug($"Setting up {nameof(ApiOneAuthorizationRequestMessageHandler)} to authorize the base url: {"https://localhost:7090/"}");
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7040" },
scopes: new[] { "api://aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/access_as_user" });
}
}
Api 2:
// This message handler handles calls to the api at the endpoint "https://localhost:7090". Check out the scope and audience through https://jwt.io
// "aud": "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
// "scp": "access_as_user",
public class ApiTwoAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public ApiTwoAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7090" },
scopes: new[] { "api://bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/access_as_user" });
}
}
MS 图形 API:
// This message handler handles calls to Microsoft graph.
// "aud": "00000003-0000-0000-c000-000000000000"
// "scp": "Calendars.ReadWrite email MailboxSettings.Read openid profile User.Read",
public class GraphApiAuthorizationRequestMessageHandler : AuthorizationMessageHandler
{
public GraphApiAuthorizationRequestMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager
)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com" },
scopes: new[] { "User.Read", "MailboxSettings.Read", "Calendars.ReadWrite" });
}
}
现在,使用上面的端点 AuthorizationMessageHandler 为每个端点注册一个命名的 HttpClient。在 Program.cs 中执行此操作:
名为“ProductsApi”的 HttpClient
//register the AuthorizationRequestMessageHandler
builder.Services.AddScoped<ApiOneAuthorizationRequestMessageHandler>();
//register the named HttpClient
builder.Services.AddHttpClient("ProductsApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7040"))
.AddHttpMessageHandler<ApiOneAuthorizationRequestMessageHandler>();
名为“MarketingApi”的 HttpClient:
builder.Services.AddScoped<ApiTwoAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MarketingApi",
httpClient => httpClient.BaseAddress = new Uri("https://localhost:7090"))
.AddHttpMessageHandler<ApiTwoAuthorizationRequestMessageHandler>();
名为“MSGraphApi”的 HttpClient
builder.Services.AddScoped<GraphApiAuthorizationRequestMessageHandler>();
builder.Services.AddHttpClient("MSGraphApi",
httpClient => httpClient.BaseAddress = new Uri("https://graph.microsoft.com"))
.AddHttpMessageHandler<GraphApiAuthorizationRequestMessageHandler>();
注册指定的 HttpClient 后,将 Msal 与 AzureAd 应用程序设置一起注册到 Program.cs 中。
无客户用户索赔的 Msal 注册:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});
如果您通过 GraphApi 按照 Microsoft Doc 进行自定义用户帐户声明,则您的添加 Msal 应如下所示:
使用自定义用户声明进行 Msal 注册:
builder.Services.AddMsalAuthentication<RemoteAuthenticationState, RemoteUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
})
.AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, GraphUserAccountFactory>();
要使用 GraphServiceClient,需要 GraphClientFactory。它将需要使用 IHttpClientFactory 创建正确命名的 HttpClient(例如 MSGraphApi)。
图客户端工厂:
public class GraphClientFactory
{
private readonly IAccessTokenProviderAccessor accessor;
private readonly IHttpClientFactory httpClientFactory;
private readonly ILogger<GraphClientFactory> logger;
private GraphServiceClient graphClient;
public GraphClientFactory(IAccessTokenProviderAccessor accessor,
IHttpClientFactory httpClientFactory,
ILogger<GraphClientFactory> logger)
{
this.accessor = accessor;
this.httpClientFactory = httpClientFactory;
this.logger = logger;
}
public GraphServiceClient GetAuthenticatedClient()
{
HttpClient httpClient;
if (graphClient == null)
{
httpClient = httpClientFactory.CreateClient("MSGraphApi");
graphClient = new GraphServiceClient(httpClient)
{
AuthenticationProvider = new GraphAuthProvider(accessor)
};
}
return graphClient;
}
}
您还需要在 Program.cs 中注册 GraphClientFactory。
builder.Services.AddScoped<GraphClientFactory>();
要访问 Marketing Api,请注入 IHttpClientFactory 并创建一个命名的 HttpClient。
@inject IHttpClientFactory httpClientFactory
<h3>Example Component</h3>
@code {
protected override async Task OnInitializedAsync()
{
try {
var httpClient = httpClientFactory.CreateClient("MarketingApi");
var resp = await httpClient.GetFromJsonAsync<APIResponse>("api/Function1");
FunctionResponse = resp.Value;
Console.WriteLine("Fetched " + FunctionResponse);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
现在,通过访问 MarketingApi,您还可以通过使用此 MSFT 教程页面上描述的组件,使用 Graph Api 访问您的日历:
[第 4 步 - 显示日历事件][1]
访问 ProductsApi 与访问 MarketingApi 非常相似。
我希望这可以帮助人们在 Blazor Webassemble 中使用正确的访问令牌访问 Api。