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

基于Identity Server 4,如何通过用户名密码端点颁发Bearer Token?

嘿,很高兴你在基于IdentityServer4搭建身份服务!针对你的问题,咱们一步步拆解清楚:

先明确:TokenClient的角色

首先得理清——TokenClient是客户端侧的工具(来自IdentityModel.Client命名空间),用来帮你调用身份服务器的令牌端点、请求Bearer Token,它本身并不是服务器端实现颁发Token的核心部分。你的服务器端需要先配置好支持用户名密码模式的授权逻辑,之后客户端才能用TokenClient来获取Token,所以它确实是客户端侧的正确方案。

下面是完整的实现步骤:


第一步:在IdentityServer4中配置服务器端逻辑

1. 定义要保护的资源

先告诉IdentityServer4,你的身份服务要管理哪些身份资源和API资源:

public static class Config
{
    // 身份资源(比如用户的基本信息)
    public static IEnumerable<IdentityResource> IdentityResources =>
        new List<IdentityResource>
        {
            new IdentityResources.OpenId(), // OpenID协议必填的标识资源
            new IdentityResources.Profile() // 用户昵称、头像等信息
        };

    // API资源(你要保护的后端API)
    public static IEnumerable<ApiResource> ApiResources =>
        new List<ApiResource>
        {
            new ApiResource("my_protected_api", "我的受保护API")
        };
}

2. 配置允许使用密码模式的客户端

创建一个客户端配置,指定它可以用资源所有者密码模式(ResourceOwnerPassword)来请求Token:

public static IEnumerable<Client> Clients =>
    new List<Client>
    {
        new Client
        {
            ClientId = "internal_app_client", // 客户端唯一标识
            ClientSecrets = { new Secret("my_client_secret_123".Sha256()) }, // 客户端密钥

            // 授权类型选择用户名密码模式
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            
            // 允许访问的资源范围
            AllowedScopes = { "my_protected_api", "openid", "profile" },
            
            // 指定颁发的Token类型为Bearer
            AccessTokenType = AccessTokenType.Jwt,
            AccessTokenLifetime = 3600, // Token有效期1小时
            AllowOfflineAccess = true // 如果需要刷新Token,开启这个选项
        }
    };

3. 实现用户验证逻辑

IdentityServer4需要知道怎么验证用户名和密码,你可以自定义验证器实现IResourceOwnerPasswordValidator接口:

public class CustomUserValidator : IResourceOwnerPasswordValidator
{
    public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        // 这里替换成你的真实用户验证逻辑(比如从数据库查询用户)
        if (context.UserName == "jacob" && context.Password == "your_secure_password")
        {
            // 验证通过,返回用户标识和自定义Claims
            context.Result = new GrantValidationResult(
                subject: "user_123", // 用户唯一ID
                authenticationMethod: "password",
                claims: new List<Claim>
                {
                    new Claim(JwtClaimTypes.Name, "Jacob Mason"),
                    new Claim(JwtClaimTypes.Email, "jacob@example.com")
                });
        }
        else
        {
            // 验证失败,返回错误信息
            context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "用户名或密码错误");
        }
    }
}

然后在Program.cs/Startup.cs中注册服务:

builder.Services.AddIdentityServer()
    .AddInMemoryIdentityResources(Config.IdentityResources)
    .AddInMemoryApiResources(Config.ApiResources)
    .AddInMemoryClients(Config.Clients)
    .AddResourceOwnerValidator<CustomUserValidator>()
    .AddDeveloperSigningCredential(); // 开发环境临时用,生产环境要替换为正式证书

第二步:用TokenClient请求Bearer Token

服务器配置好后,客户端(比如你的后端服务、桌面应用)就可以用TokenClient来获取Token了,示例代码如下:

using IdentityModel.Client;

var httpClient = new HttpClient();

// 先获取IdentityServer的配置信息(自动发现端点地址,推荐使用)
var discoveryResponse = await httpClient.GetDiscoveryDocumentAsync("https://your-identity-server-domain");
if (discoveryResponse.IsError)
{
    Console.WriteLine($"发现服务失败:{discoveryResponse.Error}");
    return;
}

// 创建TokenClient实例
var tokenClient = new TokenClient(httpClient, new TokenClientOptions
{
    Address = discoveryResponse.TokenEndpoint,
    ClientId = "internal_app_client",
    ClientSecret = "my_client_secret_123"
});

// 请求Bearer Token
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordTokenAsync(
    username: "jacob",
    password: "your_secure_password",
    scope: "my_protected_api openid profile");

if (tokenResponse.IsError)
{
    Console.WriteLine($"请求Token失败:{tokenResponse.Error}");
    return;
}

// 拿到Bearer Token后,就可以在请求API时放在Authorization头里使用
Console.WriteLine($"Access Token: {tokenResponse.AccessToken}");
Console.WriteLine($"Token Type: {tokenResponse.TokenType}"); // 这里会输出Bearer

几个关键注意事项
  • 资源所有者密码模式的适用场景:这个模式只适合你完全信任的客户端(比如内部系统),第三方客户端更推荐用授权码模式,安全性更高。
  • 生产环境证书:不要用AddDeveloperSigningCredential上线,要使用正式的SSL证书来签名JWT,避免Token被篡改。
  • ASP.NET Core Identity集成:如果你的项目已经在用ASP.NET Core Identity管理用户,可以直接用IdentityServer4.AspNetIdentity包集成,不用自己写验证器,更省心。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:25:30