最小 API Key ServiceStack 认证+授权
Minimum API Key ServiceStack authentication + authorization
我想使用 API 密钥尽可能简单地访问受保护的 ServiceStack Web 服务:
- 我不想注册用户
- 我不需要用户权限或角色
- 自定义 API 密钥权限将是一个加号:
- 能够将某些服务限制为特定的 API 密钥。
- API 密钥将直接从数据库管理
- 我需要覆盖哪些 类 或方法?有很多扩展点,但我不知道要保留什么以及重写什么:
- OrmLiteAuthRepository(基础?)
- ApiKeyAuthProvider
- AuthUserSession
我可以使用 Bearer 令牌(API 密钥)调用服务。它 returns 200 禁止.
ApiKeyAuthProvider.AuthenticateAsync():
// authRepo is ServiceStack.Auth.OrmLiteAuthRepositoryMultitenancy
var userAuth = await authRepo.GetUserAuthAsync(apiKey.UserAuthId, token).ConfigAwait();
userAuth 为 NULL,这将引发此异常:
throw HttpError.Unauthorized(ErrorMessages.UserForApiKeyDoesNotExist.Localize(authService.Request));
我将我的 API 密钥存储在 SQL 数据库中的 'ApiKey' table 中:
public override void Configure(Container 容器)
{
字符串 connectionString = GetConnectionStringByName("Main");
// 创建并注册一个默认配置为使用 Live DB 的 OrmLite 数据库工厂
var dbFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider);
container.Register(dbFactory);
// Tell ServiceStack you want to persist User Auth Info in SQL Server
container.Register<IAuthRepository>(c => new OrmLiteAuthRepository(dbFactory) { UseDistinctRoleTables = true });
// It’s safe to always call this in your AppHost as it’s just ignored if you already have the tables created
container.Resolve<IAuthRepository>().InitSchema();
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[]
{
new ApiKeyAuthProvider(AppSettings) {RequireSecureConnection = false}
}));
}
API Key AuthProvider 可能不适合您的 use-case,因为它旨在为注册用户生成 API 密钥,为他们提供另一种调用受保护 APIs 的方法.
为了能够使用 ServiceStack 的 built-in Auth API Key Auth Provider 对此进行建模,我仍然会有一个注册的 AuthProvider 和代表将使用 API 密钥的客户端的用户。
但不是提供用户注册功能,而是手动将它们添加到数据库中,然后 Generating API Keys for Existing Users。
您需要 configure your preferred RDBMS 将 API 密钥和用户存储在:
[assembly: HostingStartup(typeof(MyApp.ConfigureDb))]
public class ConfigureDb : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) =>
services.AddSingleton<IDbConnectionFactory>(
new OrmLiteConnectionFactory(
context.Configuration.GetConnectionString("DefaultConnection"),
SqliteDialect.Provider)));
}
配置 ServiceStack 的 Auth 功能 配置 API Key AuthProvider:
[assembly: HostingStartup(typeof(MyApp.ConfigureAuth))]
public class ConfigureAuth : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureAppHost(appHost =>
{
appHost.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new ApiKeyAuthProvider(appHost.AppSettings) {
RequireSecureConnection = false,
SessionCacheDuration = TimeSpan.FromMinutes(10),
}
}));
});
}
然后为您要允许访问的客户端配置 RDBMS OrmLiteAuthRepository pre-populated,然后在启动时为它们生成任何缺少的 API 密钥:
[assembly: HostingStartup(typeof(MyApp.ConfigureAuthRepository))]
public class ConfigureAuthRepository : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => services.AddSingleton<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())))
.ConfigureAppHost(appHost => {
var authRepo = appHost.Resolve<IAuthRepository>();
authRepo.InitSchema();
CreateUser(authRepo, "admin@email.com", "Admin User", "p@55wOrd",
roles: new[] { RoleNames.Admin });
CreateUser(authRepo, "admin.client@email.com", "Client Admin", "p@55wOrd",
roles: new[] { "ClientAdmin", "Client" });
CreateUser(authRepo, "client@email.com", "Client User", "p@55wOrd",
roles: new[] { "Client" });
},
afterAppHostInit: appHost => {
var authProvider = (ApiKeyAuthProvider)
AuthenticateService.GetAuthProvider(ApiKeyAuthProvider.Name);
using var db = appHost.TryResolve<IDbConnectionFactory>().Open();
var userWithKeysIds = db.Column<string>(db.From<ApiKey>()
.SelectDistinct(x => x.UserAuthId)).Map(int.Parse);
// Use custom UserAuth if configured
var userIdsMissingKeys = db.Column<string>(db.From<UserAuth>()
.Where(x => userWithKeysIds.Count == 0 || !userWithKeysIds.Contains(x.Id))
.Select(x => x.Id));
var authRepo = (IManageApiKeys)appHost.TryResolve<IAuthRepository>();
foreach (var userId in userIdsMissingKeys)
{
var apiKeys = authProvider.GenerateNewApiKeys(userId);
authRepo.StoreAll(apiKeys);
}
});
// Add initial Users to the configured Auth Repository
public void CreateUser(IAuthRepository authRepo, string email, string name, string password, string[] roles)
{
if (authRepo.GetUserAuthByUserName(email) == null)
{
var newAdmin = new AppUser { Email = email, DisplayName = name };
var user = authRepo.CreateUserAuth(newAdmin, password);
authRepo.AssignRoles(user, roles);
}
}
}
这将允许您使用 role-based auth:
保护对不同 API 的访问
[ValidateIsAdmin]
public class AdminOnly { ... }
[ValidateHasRole("ClientAdmin")]
public class ClientAdminOnly { ... }
[ValidateHasRole("Client")]
public class AnyClient { ... }
Note: The Admin is a super user role that can access any protected API
如果您不想为您的应用程序使用所有这些 Auth 组件,您必须创建自己的 Custom Auth Provider 来实现自己的身份验证,不需要使用任何其他组件,因为它具有完整的控制请求的身份验证方式。
可以参考已有的ApiKeyAuthProvider.cs for a guide on how to implement an API Key IAuthWithRequest Auth Provider that validates the BearerToken in its PreAuthenticateAsync() method。
我想使用 API 密钥尽可能简单地访问受保护的 ServiceStack Web 服务:
- 我不想注册用户
- 我不需要用户权限或角色
- 自定义 API 密钥权限将是一个加号:
- 能够将某些服务限制为特定的 API 密钥。
- API 密钥将直接从数据库管理
- 我需要覆盖哪些 类 或方法?有很多扩展点,但我不知道要保留什么以及重写什么:
- OrmLiteAuthRepository(基础?)
- ApiKeyAuthProvider
- AuthUserSession
我可以使用 Bearer 令牌(API 密钥)调用服务。它 returns 200 禁止.
ApiKeyAuthProvider.AuthenticateAsync():
// authRepo is ServiceStack.Auth.OrmLiteAuthRepositoryMultitenancy
var userAuth = await authRepo.GetUserAuthAsync(apiKey.UserAuthId, token).ConfigAwait();
userAuth 为 NULL,这将引发此异常:
throw HttpError.Unauthorized(ErrorMessages.UserForApiKeyDoesNotExist.Localize(authService.Request));
我将我的 API 密钥存储在 SQL 数据库中的 'ApiKey' table 中:
public override void Configure(Container 容器) { 字符串 connectionString = GetConnectionStringByName("Main"); // 创建并注册一个默认配置为使用 Live DB 的 OrmLite 数据库工厂 var dbFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider); container.Register(dbFactory);
// Tell ServiceStack you want to persist User Auth Info in SQL Server
container.Register<IAuthRepository>(c => new OrmLiteAuthRepository(dbFactory) { UseDistinctRoleTables = true });
// It’s safe to always call this in your AppHost as it’s just ignored if you already have the tables created
container.Resolve<IAuthRepository>().InitSchema();
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[]
{
new ApiKeyAuthProvider(AppSettings) {RequireSecureConnection = false}
}));
}
API Key AuthProvider 可能不适合您的 use-case,因为它旨在为注册用户生成 API 密钥,为他们提供另一种调用受保护 APIs 的方法.
为了能够使用 ServiceStack 的 built-in Auth API Key Auth Provider 对此进行建模,我仍然会有一个注册的 AuthProvider 和代表将使用 API 密钥的客户端的用户。
但不是提供用户注册功能,而是手动将它们添加到数据库中,然后 Generating API Keys for Existing Users。
您需要 configure your preferred RDBMS 将 API 密钥和用户存储在:
[assembly: HostingStartup(typeof(MyApp.ConfigureDb))]
public class ConfigureDb : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context, services) =>
services.AddSingleton<IDbConnectionFactory>(
new OrmLiteConnectionFactory(
context.Configuration.GetConnectionString("DefaultConnection"),
SqliteDialect.Provider)));
}
配置 ServiceStack 的 Auth 功能 配置 API Key AuthProvider:
[assembly: HostingStartup(typeof(MyApp.ConfigureAuth))]
public class ConfigureAuth : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureAppHost(appHost =>
{
appHost.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new ApiKeyAuthProvider(appHost.AppSettings) {
RequireSecureConnection = false,
SessionCacheDuration = TimeSpan.FromMinutes(10),
}
}));
});
}
然后为您要允许访问的客户端配置 RDBMS OrmLiteAuthRepository pre-populated,然后在启动时为它们生成任何缺少的 API 密钥:
[assembly: HostingStartup(typeof(MyApp.ConfigureAuthRepository))]
public class ConfigureAuthRepository : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => services.AddSingleton<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())))
.ConfigureAppHost(appHost => {
var authRepo = appHost.Resolve<IAuthRepository>();
authRepo.InitSchema();
CreateUser(authRepo, "admin@email.com", "Admin User", "p@55wOrd",
roles: new[] { RoleNames.Admin });
CreateUser(authRepo, "admin.client@email.com", "Client Admin", "p@55wOrd",
roles: new[] { "ClientAdmin", "Client" });
CreateUser(authRepo, "client@email.com", "Client User", "p@55wOrd",
roles: new[] { "Client" });
},
afterAppHostInit: appHost => {
var authProvider = (ApiKeyAuthProvider)
AuthenticateService.GetAuthProvider(ApiKeyAuthProvider.Name);
using var db = appHost.TryResolve<IDbConnectionFactory>().Open();
var userWithKeysIds = db.Column<string>(db.From<ApiKey>()
.SelectDistinct(x => x.UserAuthId)).Map(int.Parse);
// Use custom UserAuth if configured
var userIdsMissingKeys = db.Column<string>(db.From<UserAuth>()
.Where(x => userWithKeysIds.Count == 0 || !userWithKeysIds.Contains(x.Id))
.Select(x => x.Id));
var authRepo = (IManageApiKeys)appHost.TryResolve<IAuthRepository>();
foreach (var userId in userIdsMissingKeys)
{
var apiKeys = authProvider.GenerateNewApiKeys(userId);
authRepo.StoreAll(apiKeys);
}
});
// Add initial Users to the configured Auth Repository
public void CreateUser(IAuthRepository authRepo, string email, string name, string password, string[] roles)
{
if (authRepo.GetUserAuthByUserName(email) == null)
{
var newAdmin = new AppUser { Email = email, DisplayName = name };
var user = authRepo.CreateUserAuth(newAdmin, password);
authRepo.AssignRoles(user, roles);
}
}
}
这将允许您使用 role-based auth:
保护对不同 API 的访问[ValidateIsAdmin]
public class AdminOnly { ... }
[ValidateHasRole("ClientAdmin")]
public class ClientAdminOnly { ... }
[ValidateHasRole("Client")]
public class AnyClient { ... }
Note: The Admin is a super user role that can access any protected API
如果您不想为您的应用程序使用所有这些 Auth 组件,您必须创建自己的 Custom Auth Provider 来实现自己的身份验证,不需要使用任何其他组件,因为它具有完整的控制请求的身份验证方式。
可以参考已有的ApiKeyAuthProvider.cs for a guide on how to implement an API Key IAuthWithRequest Auth Provider that validates the BearerToken in its PreAuthenticateAsync() method。