我有一个使用 KeyCloak 作为身份代理的多租户应用程序。 token中有TenantId,通过它我们就可以识别出是哪个Tenant的用户。 现在,在验证令牌时,我们使用 AddJwtBearer 和 Authority。 我希望根据 TenantId 从配置中获取该权限。
有什么办法可以实现这个目标吗?
public static class KeycloakAuthenticationExtensions
{
public static void AddKeycloakAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var jwtConfiguration = configuration.GetSection(Core.Authentication.Constants.JwtConfigurationSection).Get<JwtConfiguration>();
services.AddOptions<JwtConfiguration>().Bind(configuration.GetSection(Authentication.Constants.JwtConfigurationSection));
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = jwtConfiguration!.Authority;
options.SaveToken = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidAudiences = jwtConfiguration.Audiences,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
//https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims?view=aspnetcore-6.0#extend-or-add-custom-claims-using-iclaimstransformation
//Name claim and role claim mapping
NameClaimType = ApiConstants.PreferredUserNameClaim
};
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return context.Response.WriteAsync(context.Request.Headers[ApiConstants.AuthorizationHeader].ToString());
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
}
};
});
services.AddSingleton<ITokenProvider, KeyCloakJwtTokenProvider>();
services.AddSingleton<ITokenStore, InMemoryCachedTokenStore>();
services.AddTransient<AuthenticationHttpMessageHandler>();
services.AddHttpClient<ITokenProvider, KeyCloakJwtTokenProvider>(client =>
{
client.BaseAddress = new Uri(jwtConfiguration!.Authority + ApiConstants.TokenEndpoint);
});
}
}
我已将 jwtConfiguration 更改为租户信息列表。
对于每个请求,都会调用 AddJwtBearer 来验证令牌。根据作为令牌一部分的租户 ID,我想设置权限并验证令牌。 我期待如下所示的内容,我将能够获取 TenantId 并从配置中存储的租户列表中获取租户信息
services.AddOptions<TenantConfiguration>().Bind(configuration.GetSection(Authentication.Constants.TenantConfiguration));
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var tenant1 = jwtConfiguration!.TenantInfo.FirstOrDefault(x => x.TenantId.Equals("**tenantId**", StringComparison.OrdinalIgnoreCase));
options.Authority = royHillTenant?.Authority;
options.SaveToken = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidAudiences = royHillTenant?.Audiences,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
//https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims?view=aspnetcore-6.0#extend-or-add-custom-claims-using-iclaimstransformation
//Name claim and role claim mapping
NameClaimType = ApiConstants.PreferredUserNameClaim
};
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return context.Response.WriteAsync(context.Request.Headers[ApiConstants.AuthorizationHeader].ToString());
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
}
};
});
services.AddSingleton<ITokenProvider, KeyCloakJwtTokenProvider>();
services.AddSingleton<ITokenStore, InMemoryCachedTokenStore>();
services.AddTransient<AuthenticationHttpMessageHandler>();
services.AddHttpClient<ITokenProvider, KeyCloakJwtTokenProvider>();// client =>
//{
// client.BaseAddress = new Uri(jwtConfiguration!.Authority + ApiConstants.TokenEndpoint);
//});
}
您可以稍后在控制器或自定义中间件中使用
IOptionsMonitor
更改任何服务选项。尝试以下修改控制器中的选项
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IServiceProvider _provider;
public ValuesController(IServiceProvider provider)
{
this._provider = provider;
}
[HttpGet("test")]
public void Test()
{
var jwtoptionsMonitor = _provider.GetService<IOptionsMonitor<JwtBearerOptions>>();
var jwtoptions = jwtoptionsMonitor.Get(JwtBearerDefaults.AuthenticationScheme);
//then you can modify jwtoptions as you like
jwtoptions.Authority = "new value";
jwtoptions.TokenValidationParameters.ValidAudience = "new value";
}
}
(注意:只有使用注入Iserviceprovider来获取IoptionsMonitor才有效。如果直接注入IOptionsMonitor,则不起作用。)