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

如何用IIdentityServerBuilder的AddApiAuthorization配置Identity Server与NSwag API并获取用户声明

问题:Identity Server颁发的JWT令牌仅包含sub声明,无法获取用户名/邮箱/角色等信息

问题背景

使用Identity Server搭建认证/授权流程,通过NSwag中间件让用户在Swagger API中完成授权,访问标记有[Authorize]特性的端点。当前流程可正常跳转至Identity Server登录页,登录后返回Swagger API,但请求头中的JWT Bearer令牌仅包含用户的sub(GUID)信息,无法获取用户名、邮箱、角色等声明,无法基于声明配置访问策略。

已完成的核心配置如下:

1. NSwag OAuth2安全方案配置

services.AddMvcCore().AddApiExplorer();

services.AddOpenApiDocument(settings =>
{
    settings.Title = "MyProject Services";
    settings.Version = "1.0";
    settings.AddSecurity("oauth2", new NSwag.OpenApiSecurityScheme
    {
        Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
        Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
        AuthorizationUrl = "/connect/authorize",
        TokenUrl = "/connect/token",
        Scopes = new Dictionary<string, string>
        {
            {  "MyProjectServicesAPI", "API Access" }
        }
    });
    settings.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("oauth2"));
});

2. Startup.Configure中的OAuth2客户端设置

app.UseOpenApi();
app.UseSwaggerUi3(options =>
{
   options.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings
   {
       ClientId = "MyProjectAPI",
       ClientSecret = "mysecret",
       UsePkceWithAuthorizationCodeGrant = true
    };
});

3. Identity Server核心配置(Startup.ConfigureServices)

services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
    options.IdentityResources = new IdentityResourceCollection
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResources.Email(),
        new IdentityResource
        {
             Name = "roles",
             DisplayName = "roles",
             UserClaims = new List<string> { JwtClaimTypes.Role }
        },
        new IdentityResource
        {
            Name = "basicInfo",
            DisplayName = "basic info",
            UserClaims = new List<string> {
                JwtClaimTypes.PreferredUserName
            }
        }
    };
    options.Clients = new ClientCollection
    {
        new Client
        {
            ClientId = "MyProjectAPI",
            ClientName = "My Project Services API",
            ClientSecrets = { new Secret("mysecret".Sha256()) },
            AllowedGrantTypes = GrantTypes.Code,
            AllowAccessTokensViaBrowser = true,
            RedirectUris = { "https://localhost:44319/swagger/oauth2-redirect.html" },
            PostLogoutRedirectUris = { "https://localhost:44319/Identity/Account/Logout" },
            AllowedScopes = {
                 "basicInfo",
                 "roles",
                 "MyProjectServicesAPI",
                  IdentityServerConstants.StandardScopes.OpenId,
                  IdentityServerConstants.StandardScopes.Profile,
                  IdentityServerConstants.StandardScopes.Email,
             RequirePkce = true,
             RequireConsent = false
         }
    }
};
});

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddIdentityServerJwt()
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
        {
            ValidateIssuer = true
        };
});

4. 管道配置

app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

当前OIDC发现文档已包含所有配置的范围,但令牌仅显示MyProjectServicesAPI范围,疑问:是否需要手动将声明添加到Bearer令牌中?为何已配置范围,声明仍未显示?


解决方案

1. 修正Swagger的OAuth2范围配置

当前NSwag仅请求了MyProjectServicesAPI范围,需将所需的身份范围加入Swagger的Scopes配置,确保授权时向Identity Server请求完整的范围:

settings.AddSecurity("oauth2", new NSwag.OpenApiSecurityScheme
{
    // 保留原有Type、Flow、AuthorizationUrl、TokenUrl配置
    Scopes = new Dictionary<string, string>
    {
        { "MyProjectServicesAPI", "API Access" },
        { "openid", "OpenID Connect" },
        { "profile", "User Profile" },
        { "email", "User Email" },
        { "roles", "User Roles" },
        { "basicInfo", "Basic User Info" }
    }
});

2. 修正Identity Server客户端配置的语法错误

客户端配置中AllowedScopes列表混入了RequirePkce和RequireConsent属性,需将这两个属性移到Client的根层级:

new Client
{
    // 保留原有ClientId、ClientName、ClientSecrets等配置
    AllowedScopes = {
         "basicInfo",
         "roles",
         "MyProjectServicesAPI",
          IdentityServerConstants.StandardScopes.OpenId,
          IdentityServerConstants.StandardScopes.Profile,
          IdentityServerConstants.StandardScopes.Email
     },
     RequirePkce = true,
     RequireConsent = false
}

3. 确保Identity Server将身份声明注入访问令牌

默认情况下,Identity Server不会自动将身份资源的声明放入访问令牌,可通过两种方式实现:

方式一:在ApiResource中声明需要包含的用户声明

在AddApiAuthorization配置中添加ApiResources,指定需要注入访问令牌的用户声明:

options.ApiResources = new ApiResourceCollection
{
    new ApiResource("MyProjectServicesAPI")
    {
        UserClaims = {
            JwtClaimTypes.Subject,
            JwtClaimTypes.PreferredUserName,
            JwtClaimTypes.Email,
            JwtClaimTypes.Role
        }
    }
};

方式二:自定义ProfileService手动添加声明

创建自定义ProfileService,从用户存储中获取并注入所需声明:

public class CustomProfileService : IProfileService
{
    private readonly UserManager<ApplicationUser> _userManager;

    public CustomProfileService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        if (user == null) throw new ArgumentException("用户不存在");

        var claims = new List<Claim>
        {
            new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
            new Claim(JwtClaimTypes.Email, user.Email)
        };

        var roles = await _userManager.GetRolesAsync(user);
        claims.AddRange(roles.Select(r => new Claim(JwtClaimTypes.Role, r)));

        context.IssuedClaims.AddRange(claims);
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        context.IsActive = user != null;
    }
}

在Startup.ConfigureServices中注册该服务:

services.AddScoped<IProfileService, CustomProfileService>();

4. 简化认证中间件配置

移除多余的AddJwtBearer配置,AddIdentityServerJwt()已包含完整的JWT Bearer认证逻辑,重复配置可能导致冲突:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddIdentityServerJwt();

验证步骤

  1. 重启Identity Server和API服务
  2. 在Swagger中重新发起授权,确认授权弹窗显示所有添加的范围
  3. 调用带[Authorize]的端点,通过jwt.io解析请求头中的JWT令牌,确认包含所需的用户名、邮箱、角色等声明

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:45:44