You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core+Angular模板中Duende M2M客户端配置及C#令牌请求问题

ASP.NET Core + Angular 项目配置IdentityServer多客户端及令牌请求实现

问题背景

使用Visual Studio最新版ASP.NET Core with Angular模板创建项目,新增Windows Service调用API的需求,需通过IdentityServer获取令牌。但Postman请求令牌时始终返回“Authentication failed”,日志提示“Unknown client 'postman'”,内存中定义的客户端未生效;尝试添加.AddAspNetIdentity()又触发System.InvalidOperationException: No service for type 'Duende.IdentityServer.Stores.ISigningCredentialStore' has been registered错误。

核心问题

模板自带的.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()方法会自动配置IdentityServer使用EF数据库存储,覆盖手动添加的内存客户端、资源配置,导致Postman无法找到定义的postman客户端。

解决方案一:启用内存存储(开发/测试场景)

调整IdentityServer注册逻辑,移除自动EF存储配置,手动添加AspNetIdentity支持及签名凭证:

修改Program.cs的IdentityServer配置

替换原IdentityServer注册代码:

builder.Services.AddIdentityServer(options =>
{
    options.Events.RaiseErrorEvents = true;
    options.Events.RaiseInformationEvents = true;
    options.Events.RaiseFailureEvents = true;
    options.Events.RaiseSuccessEvents = true;
    options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddAspNetIdentity<ApplicationUser>() // 添加AspNetIdentity用户支持
.AddDeveloperSigningCredential(); // 开发环境生成临时签名证书,生产需替换为正式证书

注意:.AddDeveloperSigningCredential()仅用于开发环境,生产环境需使用持久化的签名证书(如从文件或密钥管理服务加载)。

补充Angular客户端配置

由于移除了.AddApiAuthorization()的自动配置,需在Config.Clients中添加Angular SPA的客户端配置(确保与Angular项目环境变量中的clientId一致):

// Angular SPA 客户端
new Client
{
    ClientId = "Project1_Angular", // 替换为你的Angular项目clientId
    ClientName = "Angular SPA Client",
    AllowedGrantTypes = GrantTypes.Code,
    RequirePkce = true,
    RequireClientSecret = false,
    RedirectUris = { "https://localhost:5001/authentication/login-callback" }, // 替换为你的Angular登录回调地址
    PostLogoutRedirectUris = { "https://localhost:5001/authentication/logout-callback" },
    AllowedCorsOrigins = { "https://localhost:5001" }, // 替换为你的Angular应用地址
    AllowedScopes = { "openid", "profile", "api1" }
}

解决方案二:使用数据库存储(生产场景)

若需保留EF数据库存储,可将内存中的客户端、资源配置迁移到数据库:

  1. 保留.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(),移除.AddInMemoryXXX方法。
  2. 添加初始化代码,启动时将Config中的配置写入数据库:
var app = builder.Build();

// 初始化IdentityServer配置到数据库
using (var scope = app.Services.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    var clientStore = scope.ServiceProvider.GetRequiredService<IClientStore>();
    
    // 检查并添加postman客户端
    var existingClient = await clientStore.FindClientByIdAsync("postman");
    if (existingClient == null)
    {
        var postmanClient = Config.Clients.First(c => c.ClientId == "postman");
        context.Clients.Add(postmanClient.ToEntity());
        await context.SaveChangesAsync();
    }
    
    // 同理初始化ApiScopes和IdentityResources
}

// 后续管道配置...

C#实现客户端凭证模式令牌请求

Windows Service可通过客户端凭证模式获取令牌,示例代码如下:

using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class TokenService
{
    private readonly HttpClient _httpClient;

    public TokenService(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://localhost:7229/"); // 替换为你的IdentityServer地址
    }

    public async Task<string> GetAccessTokenAsync()
    {
        var requestContent = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", "postman"),
            new KeyValuePair<string, string>("client_secret", "511536EF-F270-4058-80CA-1C89C192F69A"),
            new KeyValuePair<string, string>("scope", "api1")
        });

        var response = await _httpClient.PostAsync("connect/token", requestContent);
        response.EnsureSuccessStatusCode();

        var responseJson = await response.Content.ReadAsStringAsync();
        var tokenResult = JsonConvert.DeserializeObject<TokenResponse>(responseJson);

        return tokenResult.AccessToken;
    }
}

// 令牌响应模型
public class TokenResponse
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }

    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }
}

使用时,在Windows Service中注册HttpClient和TokenService,调用GetAccessTokenAsync获取令牌后,在API请求头中添加Authorization: Bearer {token}即可访问受保护API。

内容的提问来源于stack exchange,提问作者Andrei Dobrin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 03:51:41