繁体   English   中英

最小API Key ServiceStack认证+授权

[英]Minimum API Key ServiceStack authentication + authorization

我想使用 API 密钥尽可能简单地访问受保护的 ServiceStack web 服务:

  • 我不想注册用户
  • 我不需要用户权限或角色
  • 自定义 API 密钥权限将是一个加号:
    • 能够将某些服务限制为特定的 API 密钥。
  • API 密钥将直接从数据库管理
  • 我需要覆盖哪些类或方法? 有很多扩展点,但我不知道要保留什么以及重写什么:
    • OrmLiteAuthRepository(基础?)
    • ApiKeyAuthProvider
    • 授权用户会话

我可以使用 Bearer 令牌(API 密钥)调用服务。 它返回 200 Forbidden。

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”表中:

public override void Configure(Container container) { string 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 密钥 AuthProvider可能不适合您的用例,因为它旨在为注册用户生成 API 密钥,以便为他们提供调用受保护 API 的替代方式。

为了能够 model 使用 ServiceStack 的内置 Auth API Key Auth Provider,我仍然会有一个注册的 AuthProvider 和代表将使用 API 密钥的客户端的用户。

但不是提供用户注册功能,而是手动将它们添加到数据库中,然后Generating API Keys for Existing Users

您需要配置首选 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)));
}

使用API Key AuthProvider配置 ServiceStack 的Auth Feature

[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预填充您要允许访问的客户端,然后在启动时为它们生成任何缺少的 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);
        }
    }
}

这将使您可以使用基于角色的身份验证来保护对不同 API 的访问:

[ValidateIsAdmin]
public class AdminOnly { ... }

[ValidateHasRole("ClientAdmin")]
public class ClientAdminOnly { ... }

[ValidateHasRole("Client")]
public class AnyClient { ... }

注意: Admin是超级用户角色,可以访问任何受保护的 API

如果您不希望您的应用程序使用所有这些 Auth 组件,您必须创建自己的Custom Auth Provider来实现自己的 Authentication,它不需要使用任何其他组件,因为它可以完全控制请求的身份验证方式.

您可以参考现有的ApiKeyAuthProvider.cs以获取有关如何实现 API Key IAuthWithRequest Auth Provider的指南,该提供程序在其PreAuthenticateAsync() 方法中验证 BearerToken。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM