我正在用ASP构建一个web应用程序。NET Core 6。
我有:
- 前端。客户端-带有UI的Blazor WebAssembly
- 前端。服务器-ASP。NET Core,托管Blazor WebAssembly
- Web Api-远程REST服务
- gRpc服务-远程gRpc
- 身份提供程序-使用Duende的Duende项目。Bff。Yarp
我的前端。客户端被配置为调用自己的BFF(Frontend.Server(,而服务器则使用Duende将调用转发到REST和gRpc服务。Bff。YARP
对REST服务的调用按预期工作:客户端按照文档自动传递令牌
我的问题是对gRpc的调用,它似乎没有使用正确的HttpClient和AntiForgeryToken和Access Token
我知道我在某个地方缺少一些设置,但我找不到任何关于如何将Duende与gRpcWebClient一起使用的示例。
我的前端。客户端配置包含:
builder.Services.AddScoped<AuthenticationStateProvider, BffAuthenticationStateProvider>();
builder.Services.AddTransient<AntiforgeryHandler>();
builder.Services.AddHttpClient("backend", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<AntiforgeryHandler>();
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("backend"));
builder.Services.AddSingleton(services => {
var backendUrl = new Uri(builder.HostEnvironment.BaseAddress);
var channel = GrpcChannel.ForAddress(backendUrl, new GrpcChannelOptions {
HttpHandler = new GrpcWebHandler(new HttpClientHandler()),
});
return new Commenter.CommenterClient(channel);
});
我的前端。服务器配置包含:
builder.Services.AddBff();
var proxyBuilder = builder.Services.AddReverseProxy().AddTransforms<AccessTokenTransformProvider>();
// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddAuthentication(options => {
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
options.DefaultSignOutScheme = "oidc";
})
.AddCookie("cookie", options => {
options.Cookie.Name = "__Host-blazor";
options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("oidc", options => {
options.Authority = "https://localhost:5007";
options.ClientId = "photosharing.bff";
options.ClientSecret = "A9B27D26-E71C-4C53-89A8-3DAB53CE1854";
options.ResponseType = "code";
options.ResponseMode = "query";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("photosrest");
options.Scope.Add("commentsgrpc");
options.Scope.Add("offline_access");
options.MapInboundClaims = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
});
//code omitted for brevity
app.UseAuthentication();
app.UseBff();
app.UseAuthorization();
app.MapBffManagementEndpoints();
app.MapReverseProxy().AsBffApiEndpoint();
用于读取配置的appsettings.json文件包含:
"ReverseProxy": {
"Routes": {
"photosrestroute": {
"ClusterId": "photosrestcluster",
"Match": {
"Path": "/photos/{*any}"
},
"Metadata": {
"Duende.Bff.Yarp.TokenType": "User"
}
},
"commentsgrpcroute": {
"ClusterId": "commentsgrpccluster",
"Match": {
"Path": "/comments.Commenter/{*any}"
},
"Metadata": {
"Duende.Bff.Yarp.TokenType": "User"
}
}
},
"Clusters": {
"photosrestcluster": {
"Destinations": {
"photosrestdestination": {
"Address": "https://localhost:5003/"
}
}
},
"commentsgrpccluster": {
"Destinations": {
"commentsgrpdestination": {
"Address": "https://localhost:5005/"
}
}
}
}
}
当我的客户调用gRpc时,我收到401未经授权的响应和Duende。Bff记录了AntiForgery检查没有通过,事实上,请求没有带有X-CSRF 1的标头(而对REST Api的调用是通过的(。这表明gRpc客户端没有使用Duende使用的HTTP客户端
如何将gRpc客户端连接到Duende?
注意:在引入身份验证/授权位之前,我直接使用YARP,对gRpc的调用运行良好。当我添加Duende时,它就坏了。
问题出在AntiforgeryHandler上,因为我没有将它添加到gRpcChannel的HttpHandlers链中。我所做的是
- 向我的AntiforgeryHandler添加一个构造函数,以接受一个innerhandler并将其传递给它的基类
- 将我的AntiforgeryHandler附加到构造grpc客户端的HttpHandlers链
AntiforgeryHandler变为:
namespace PhotoSharingApplication.Frontend.Client.DuendeAuth;
public class AntiforgeryHandler : DelegatingHandler {
public AntiforgeryHandler() { }
public AntiforgeryHandler(HttpClientHandler innerHandler) : base(innerHandler) { }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
request.Headers.Add("X-CSRF", "1");
return base.SendAsync(request, cancellationToken);
}
}
grpc客户端在我的前端的构建。客户项目变成:
builder.Services.AddSingleton(services => {
var backendUrl = new Uri(builder.HostEnvironment.BaseAddress);
var channel = GrpcChannel.ForAddress(backendUrl, new GrpcChannelOptions {
HttpHandler = new GrpcWebHandler(new AntiforgeryHandler(new HttpClientHandler())),
});
return new Commenter.CommenterClient(channel);
});