获取数据返回对受Azure AD保护的asp.net核心API的非结构化401访问
我是新来webassembly blazor的,我花了太多时间想弄清楚这里出了什么问题,但我无法管理 我有以下情况:获取数据返回对受Azure AD保护的asp.net核心API的非结构化401访问,api,azure-active-directory,bearer-token,blazor-webassembly,Api,Azure Active Directory,Bearer Token,Blazor Webassembly,我是新来webassembly blazor的,我花了太多时间想弄清楚这里出了什么问题,但我无法管理 我有以下情况: Asp.net API在Azure AD中注册并受保护 公开作用域为AcessApi且状态为已启用的API 将客户端应用程序添加到授权客户端应用程序中 令牌配置检查访问令牌和ID令牌 以及一个调用API的客户端应用程序,在webassembly blazor 客户端应用程序已在Azure AD中注册 客户端API权限已授予使用我的客户端API的权限 具有正确的作用域AccessA
AcessApi
且状态为已启用的API
授权客户端应用程序中
webassembly blazor
AccessApi
curl
进行了测试,从swagger界面抓取了令牌,效果非常好
curl -X GET "http://localhost:9400/api/getdata" -H "accept: text/plain" -H "Authorization: Bearer XX"
但是,当我的客户端应用程序尝试访问API时,会弹出一个凭据登录页面,我可以看到正在检索浏览器栏上的令牌ID,调用API时,应用程序会记录错误未授权
客户端应用程序的程序类:
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
//builder.Logging.SetMinimumLevel(LogLevel.Debug);
////builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("AccessApi",
client => client.BaseAddress = new Uri("http://localhost:9400"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("AccessApi"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
});
await builder.Build().RunAsync();
}
在appsettings.json
a中定义了API的客户端id和租户id,但没有作用域,因为它们是在CustomAuthorizationMessageHandler
类中定义的:
{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/<tenant_id>",
"ClientId": "<clientid>",
"CallbackPath": "/signin-oidc",
"ValidateAuthority": "true"
}
}
控制台日志
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
Authorization was successful.
info: System.Net.Http.HttpClient.AccessApi.ClientHandler[100]
Sending HTTP request GET http://localhost:9400/api/getdata
:9400/customer-manager/api/getdata:1 Failed to load resource: the server responded with a status of 401 (Unauthorized)
这里可能出了什么问题?
有没有办法打印返回令牌
更新
我使用Postman测试了API,其中auth-Grant类型是隐式的
,成功登录后,我将令牌存储在变量上,并作为承载者
传入头部,API返回401
未授权。我解码了令牌,它包含正确的作用域AccessApi
,具有正确的clientId。这里可能有什么问题?如果您想在一个blazor webassembly项目中调用Microsoft graph和自定义API,我们可以通过创建不同的HTTP客户端来调用不同的API来实现
比如说
- 注册服务器API应用程序
- 为服务器API应用程序注册AAD应用程序
- 注册客户端应用程序
- 注册客户端应用程序
- 添加API权限。(API应用程序权限)
- 配置API应用程序
请在Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
public void配置服务(IServiceCollection服务)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims=false;
services.AddCors(选项=>
{
options.AddDefaultPolicy(
builder=>builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BeareAuthenticationScheme)
.AddAzureADBearer(options=>Configuration.Bind(“AzureAd”,options));
Configure(AzureADDefaults.JwtBearerAuthenticationScheme,选项=>
{
选项。权限+=“/v2.0”;
options.TokenValidationParameters=新的TokenValidationParameters
{
ValidIssuers=new[]{
$"https://sts.windows.net/{配置[“AzureAD:TenantId”]}/“,
$"https://login.microsoftonline.com/{配置[“AzureAD:TenantId”]}/v2.0
},
RoleClaimType=“角色”,
//web API接受客户机ID(options.acquisition)和API://{ClientID}作为访问群体。
有效期=新[]
{
选择,观众,
$“api://{options.viewer}”
}
};
});
....
}
public void配置(IApplicationBuilder应用程序、IWebHostEnvironment环境)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c=>
{
c、 OAuthClientId(配置[“Swagger:ClientId]”);
c、 OAuthScope分隔符(“”);
c、 OAuthAppName(“受保护的Api”);
c、 SwaggerEndpoint(“/swagger/v1/swagger.json”,“我的API v1”);
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(端点=>
{
endpoints.MapControllers();
});
}
- 配置客户端应用程序
为图形API和自定义API创建自定义AuthorizationMessageHandler
//自定义API
使用Microsoft.AspNetCore.Components;
使用Microsoft.AspNetCore.Components.WebAssembly.Authentication;
公共类CustomAuthorizationMessageHandler:AuthorizationMessageHandler
{
公共CustomAuthorizationMessageHandler(IAccessTokenProvider提供程序,
导航管理器(导航管理器)
:base(提供程序、navigationManager)
{
配置处理程序(
authorizedUrls:new[]{”“},
作用域:新[]{“API应用程序作用域”});
}
}
将以下代码添加到program.cs
公共类程序
{
公共静态异步任务主(字符串[]args)
{
var builder=WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add最后,我发现问题出在服务器端ASP.net core上,我在启动时在配置服务
中验证令牌:
// For token parameters validation
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.Audience = "<xx>"; // Application id
o.Authority = "https://login.microsoftonline.com/<xx>"; // Tenant ID
//Token validation
o.TokenValidationParameters = new TokenValidationParameters {ValidateIssuerSigningKey = false, ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true};
});
//用于令牌参数验证
服务
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
// custom API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<your web API url>" },
scopes: new[] { "the API app scope" });
}
}
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
// register HTTP client to call our own api
builder.Services.AddHttpClient("MyAPI", client => client.BaseAddress = new Uri("<your web API url>"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the API app scope>");
});
await builder.Build().RunAsync();
}
}
@inject IHttpClientFactory _clientFactory
var httpClient = _clientFactory.CreateClient("<the client name you register>");
await apiClient.GetStringAsync("path");
// For token parameters validation
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.Audience = "<xx>"; // Application id
o.Authority = "https://login.microsoftonline.com/<xx>"; // Tenant ID
//Token validation
o.TokenValidationParameters = new TokenValidationParameters {ValidateIssuerSigningKey = false, ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true};
});