ABP vNext + OpenIddict:自定义 OAuth2/OpenID Connect 认证策略 🚀
🧠 背景与核心设计思路
大型 SaaS 系统常见需求:
- 自定义身份来源(API Key、Device Flow)
- 多租户隔离与 SSO
- 精细化 Scope/资源管理
ABP 的 OpenIddict 模块提供 Handler 模型 插槽,轻松插入自定义授权逻辑。
🛠 依赖注入与启动配置
public class AuthServerModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
// ➤ 注册 OpenIddict 核心 + Server + Validation
services.AddOpenIddict()
.AddCore(options => {
/* 实体存储等 */ })
.AddServer(options => {
/* 稍后配置 */ })
.AddValidation(options => {
/* 稍后配置 */ });
// ➤ 多租户解析
services.Configure<AbpTenantResolveOptions>(opts =>
{
opts.Resolvers.Insert(0, new HeaderTenantResolveContributor());
opts.Resolvers.Insert(1, new DomainTenantResolveContributor());
});
}
}
🔑 系统配置:注册 Token 授权管道
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddServer(options =>
{
// —— 端点 ——
options.SetTokenEndpointUris("/connect/token")
.SetAuthorizationEndpointUris("/connect/authorize")
.SetDeviceEndpointUris("/connect/device");
// —— Grant & Scope ——
options.RegisterGrantType("api_key_grant")
.AllowPasswordFlow()
.AllowClientCredentialsFlow()
.AllowRefreshTokenFlow()
.AllowExtensionGrantType("api_key_grant")
.SetDefaultScopes("api", "profile");
// —— 有效期 ——
options.SetAccessTokenLifetime(TimeSpan.FromHours(2))
.SetRefreshTokenLifetime(TimeSpan.FromDays(7));
// —— ASP.NET Core 集成 ——
options.UseAspNetCore()
.EnableTokenEndpointPassthrough();
// —— 自定义 Handler ——
options.AddEventHandler<HandleTokenRequestContext>(cfg =>
cfg.UseScopedHandler<ApiKeyGrantHandler>()
.SetOrder(OpenIddictServerHandlers.Authentication.ValidateTokenRequest.Descriptor.Order + 1)
.SetFilter(ctx => ctx.Request.GrantType == "api_key_grant"));
});
builder.AddValidation(options =>
{
options.UseLocalServer();
options.UseAspNetCore();
});
});
🔧 自定义授权处理器:ApiKeyGrantHandler
public class ApiKeyGrantHandler : IOpenIddictServerHandler<HandleTokenRequestContext>
{
private readonly IApiKeyValidator _apiKeyValidator;
private readonly ICurrentTenant _currentTenant;
private readonly ILogger<ApiKeyGrantHandler> _logger;
public ApiKeyGrantHandler(
IApiKeyValidator apiKeyValidator,
ICurrentTenant currentTenant,
ILogger<ApiKeyGrantHandler> logger)
{
_apiKeyValidator = apiKeyValidator;
_currentTenant = currentTenant;
_logger = logger;
}
public async ValueTask HandleAsync(HandleTokenRequestContext context)
{
using var scope = _logger.BeginScope(new {
GrantType = "api_key" });
try
{
var apiKey = context.Request.GetParameter("api_key")?.ToString();
if (string.IsNullOrWhiteSpace(apiKey))
{
context.Reject(Errors.InvalidRequest, "Missing API Key");
return;
}
var userId = await _apiKeyValidator.ValidateAsync(apiKey);
if (string.IsNullOrEmpty(userId))
{
context.Reject(Errors.InvalidGrant, "Invalid API Key");
return;
}
var tenantId = _currentTenant.Id?.ToString() ?? "default";
var claims =

最低0.47元/天 解锁文章
894

被折叠的 条评论
为什么被折叠?



