我正在尝试从 .Net 6 解决方案中的 Identity Server 4 获取访问令牌。我有 3 个项目正在运行: Identity Server从中获取访问令牌 Movies.API 用于访问受保护的资源 Movies.Client 调用 api 并使用我使用 http 消息处理程序从 Identity Server 获取的访问令牌获取安全资源
这是我的身份服务器配置代码(相关客户端):
public class Config
{
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "movieClient",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "movieAPI" } // as the name in ApiScopes static list
},
new Client
{
ClientId = "movies_mvc_client",
ClientName = "Movies MVC Web App",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = false,
AllowRememberConsent = false,
RedirectUris = new List<string>()
{
"https://localhost:5002/signin-oidc"
},
PostLogoutRedirectUris = new List<string>()
{
"https://localhost:5002/signout-callback-oidc"
},
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
}
}
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("movieAPI","Movie API")
};
// rest of the code ...
这是服务 GetMovies() 中注入的 IHttpClientFactory :
public async Task<IEnumerable<Movie>> GetMovies()
{
// ** OPTION 1 **
var httpClient = _httpClientFactory.CreateClient("MovieAPIClient");
var request = new HttpRequestMessage(HttpMethod.Get, "/api/movies/");
// using the handler
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var movieList = JsonConvert.DeserializeObject<List<Movie>>(content);
return movieList;
...
最后是在 Movie.Client Program.cs 中注册 HttpClient :
// 1- create an httpclient used for accessing in Movies.API
builder.Services.AddTransient<AuthenticationDelegatingHandler>();
builder.Services.AddHttpClient("MovieAPIClient", client =>
{
client.BaseAddress = new Uri("https://localhost:5001/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>();
// 2- create an httpclient used for accessing in the IDP
builder.Services.AddHttpClient("IDPClient", client =>
{
client.BaseAddress = new Uri("https://localhost:5005/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>();
builder.Services.AddSingleton(new ClientCredentialsTokenRequest
{
Address = "https://localhost:5005/connect/token",
ClientId = "movieClient",
ClientSecret = "secret",
Scope = "movieAPI"
});
现在正在调试中:每次点击继续按钮时,我都会再次返回到处理程序中的 SendAsync() 。
有人可以帮我解决这个问题吗? 谢谢你。
您面临的问题是无限循环,因为您对获取电影的请求和获取 accessToken 的请求使用相同的
httpClient
。
使用 httpClient 的每个请求都会经过
AuthenticationDelegatingHandler
。
为了解决您的问题,这就是我的解决方案。
public class AuthenticationDelegatingHandler : DelegatingHandler{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ClientCredentialsTokenRequest _tokenRequest;
public AuthenticationDelegatingHandler(IHttpClientFactory httpClientFactory, ClientCredentialsTokenRequest tokenRequest){
_httpClientFactory = httpClientFactory;
_tokenRequest = tokenRequest;
}
protected override async Task<IHttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){
// HttpClientFactory will create a new httpClient different from the one registered in Program.cs
var httpClient = _httpClientFactory.CreateClient();
var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(_tokenRequest);
if(tokenResponse.IsError){
throw new HttpRequestException("Something went wrong while requesting the access token");
}
request.SetBearerToken(tokenResponse.AccessToken);
return await base.SendAsync(request, cancellationToken);
}
}
我发现问题了!,这是因为我在“IDPClient”上使用处理程序,它应该由 Identity Server 4 端处理。
之前
// 2- create an httpclient used for accessing in the IDP
builder.Services.AddHttpClient("IDPClient", client =>
{
client.BaseAddress = new Uri("https://localhost:5005/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>(); // remove the handler
之后
// 2- create an httpclient used for accessing in the IDP
builder.Services.AddHttpClient("IDPClient", client =>
{
client.BaseAddress = new Uri("https://localhost:5005/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
});
它会起作用的!