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

在同一ASP.NET Web应用中实现两种认证方式的方法

同时支持用户认证与客户端凭据认证的实现方案

核心逻辑是在认证管道中注册多个独立的认证方案,而非自定义AuthorizeAttribute,通过原生特性指定端点使用对应认证方式即可实现需求。

1. 注册多认证方案到管道

以ASP.NET Core为例(Owin框架逻辑类似,仅配置方式略有差异),分别注册用户认证(如JWT)和客户端凭据认证两种方案:

注册用户JWT认证方案

services.AddAuthentication()
    .AddJwtBearer("UserJwt", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"]))
        };
    });

注册客户端ID/密钥认证方案

需要自定义认证Handler来实现客户端凭据的验证逻辑,先定义方案配置和Handler:

// 自定义认证选项
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public string ClientIdHeaderName { get; set; } = "X-Client-Id";
    public string ApiKeyHeaderName { get; set; } = "X-Client-Key";
    public IDictionary<string, string> ValidClients { get; set; } = new Dictionary<string, string>();
}

// 自定义认证Handler
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
    public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock) { }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // 从请求头读取客户端ID和密钥
        if (!Request.Headers.TryGetValue(Options.ClientIdHeaderName, out var clientId) ||
            !Request.Headers.TryGetValue(Options.ApiKeyHeaderName, out var apiKey))
        {
            return Task.FromResult(AuthenticateResult.Fail("Missing client credentials"));
        }

        // 验证凭据合法性(可从数据库/配置中心读取合法凭据)
        if (Options.ValidClients.TryGetValue(clientId, out var validKey) && validKey == apiKey)
        {
            var claims = new[] { new Claim("ClientId", clientId) };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
            return Task.FromResult(AuthenticateResult.Success(ticket));
        }

        return Task.FromResult(AuthenticateResult.Fail("Invalid client credentials"));
    }
}

然后将该方案注册到管道:

services.AddAuthentication()
    .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ClientCredentials", options =>
    {
        // 配置合法的客户端ID/密钥对,实际项目建议从配置文件或数据库加载
        options.ValidClients.Add("AutomatedReportClient", "K2xL9zQ7wE4rT1yU");
    });

2. 为端点指定对应认证方案

直接使用原生AuthorizeAttribute,通过AuthenticationSchemes属性指定该端点使用的认证方案:

用户访问的端点

[Authorize(AuthenticationSchemes = "UserJwt")]
[HttpGet("user/orders")]
public IActionResult GetUserOrders()
{
    var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    return Ok($"Orders for user {userId}");
}

自动化客户端访问的端点

[Authorize(AuthenticationSchemes = "ClientCredentials")]
[HttpPost("automated/sync-data")]
public IActionResult SyncAutomatedData([FromBody] SyncRequest request)
{
    var clientId = User.FindFirst("ClientId")?.Value;
    // 处理客户端同步逻辑
    return Ok($"Sync completed for client {clientId}");
}

3. 可选:用策略实现灵活访问控制

如果需要某些端点允许两种认证方式,或添加额外权限校验,可以定义授权策略:

services.AddAuthorization(options =>
{
    options.AddPolicy("UserOrClient", policy =>
    {
        policy.AuthenticationSchemes.Add("UserJwt");
        policy.AuthenticationSchemes.Add("ClientCredentials");
        policy.RequireAuthenticatedUser();
        // 可添加额外规则,比如客户端需要特定权限
        policy.RequireClaim("ClientId", "AutomatedReportClient");
    });
});

然后在端点上使用该策略:

[Authorize(Policy = "UserOrClient")]
[HttpGet("shared/stats")]
public IActionResult GetSharedStats()
{
    return Ok("Shared statistics data");
}

Owin框架适配说明

Owin中需注册多个认证中间件,每个中间件指定唯一的AuthenticationType:

// 注册JWT认证中间件
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
    AuthenticationType = "UserJwt",
    TokenValidationParameters = new TokenValidationParameters
    {
        // 配置同ASP.NET Core
    }
});

// 注册客户端凭据认证中间件(自定义中间件逻辑类似Core的Handler)
app.UseMiddleware<ClientCredentialsAuthenticationMiddleware>(new ClientCredentialsOptions
{
    AuthenticationType = "ClientCredentials",
    ValidClients = new Dictionary<string, string> { { "Client1", "Secret1" } }
});

控制器中通过AuthorizeAttribute的AuthenticationTypes指定方案:

[Authorize(AuthenticationTypes = "UserJwt")]
public class UserApiController : ApiController { /* ... */ }

[Authorize(AuthenticationTypes = "ClientCredentials")]
public class AutomatedApiController : ApiController { /* ... */ }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:27:10