【问题标题】:ServiceStack caching users roles and permissions approachServiceStack 缓存用户角色和权限的方法
【发布时间】:2019-02-08 08:56:35
【问题描述】:

使用 AuthFeature / AuthUserSession 插件,我们可以在每个请求的 PopulateSessionFilter 中使用用户角色、权限等填充会话。

Plugins.Add(new AuthFeature(() => new AuthUserSession(), 
                new IAuthProvider[] {
                    new CredentialsAuthProvider(AppSettings), 
                    new NetCoreIdentityAuthProvider(AppSettings) 
                    {
                        PopulateSessionFilter = (session, principal, req) => 
                        {
                            //Example of populating ServiceStack Session Roles for EF Identity DB
                            var userManager = req.TryResolve<UserManager<ApplicationUser>>();
                            var user = userManager.FindByIdAsync(session.Id).Result;
                            var roles = userManager.GetRolesAsync(user).Result;
                            session.Roles = roles.ToList();
                        }
                    }, 
                }));

有没有办法根据已配置的内容将其存储在缓存、MemoryCacheClient 或 Redis 中,因此不必在此处进行数据库调用,或者是在 userManager 本身(或任何存储库)中实现缓存解决方案的情况编写代码来获取此信息)?

【问题讨论】:

  • forums.servicestack.net/t/… 类似,但我希望避免添加到 JWT 令牌中。
  • 看起来我可以使用 HostContext.TryResolve() 来获取缓存。
  • 这样就可以访问注册的缓存客户端,也可以使用req.GetMemoryCacheClient()req.TryResolve&lt;MemoryCacheClient&gt;()访问本地内存缓存客户端。

标签: servicestack


【解决方案1】:

我在this commit 中包含了对访问和缓存 ASP.NET 身份用户角色的更好支持。此更改在最新的 v5.4.1 预发行版 now on MyGet 中可用。

var userManager = req.TryResolve<UserManager<ApplicationUser>>();
var user = userManager.FindByIdAsync(session.Id).Result;
var roles = userManager.GetRolesAsync(user).Result;

虽然这可行,但它使用“同步而不是异步”,这是不鼓励的,因为它在许多用例中存在问题,效率低于同步,并且有限的 API 会强制执行多个数据库调用。

新的IDbConnection.GetIdentityUserRolesById(userId) API 现在是我们推荐的更高效的 API,它解决了上述问题并利用单个 DB 调用仅获取 ASP.NET 身份用户角色。

这是一种适用于任何IDbConnection 的扩展方法,为了避免自己从连接字符串打开新的数据库连接,您可以通过将其添加到您的应用程序来在新范围内使用 EF 的ApplicationDbContext 数据库连接:

public static class AppExtensions
{
    public static T DbExec<T>(this IServiceProvider services, Func<IDbConnection, T> fn) => 
        services.DbContextExec<ApplicationDbContext,T>(ctx => {
            ctx.Database.OpenConnection(); return ctx.Database.GetDbConnection(); }, fn);
}

这提供了一个通用 API,可以轻松利用您的应用上下文数据库连接。

这样,您可以使用更简洁有效的替代方法获取用户角色:

new NetCoreIdentityAuthProvider(AppSettings) 
{
    PopulateSessionFilter = (session, principal, req) => 
    {
        session.Roles = ApplicationServices.DbExec(db => db.GetIdentityUserRolesById(session.Id));
    }
},

为了避免在每个请求上命中数据库,您可以通过使用本地内存缓存客户端来缓存结果:

new NetCoreIdentityAuthProvider(AppSettings) 
{
    PopulateSessionFilter = (session, principal, req) => 
    {
        session.Roles = req.GetMemoryCacheClient().GetOrCreate(
            IdUtils.CreateUrn(nameof(session.Roles), session.Id),
            TimeSpan.FromMinutes(20),
            () => ApplicationServices.DbExec(db => db.GetIdentityUserRolesById(session.Id)));
    }
},

这将避免在 20 分钟内访问数据库以获取该用户的用户角色。

上面使用了MemoryCacheClient,它避免了任何I/O,因为它保存在内存中,而是使用注册的ICacheClient,将GetMemoryCacheClient()重命名为GetCacheClient(),即:

new NetCoreIdentityAuthProvider(AppSettings) 
{
    PopulateSessionFilter = (session, principal, req) => 
    {
        session.Roles = req.GetCacheClient().GetOrCreate(
            IdUtils.CreateUrn(nameof(session.Roles), session.Id),
            TimeSpan.FromMinutes(20),
            () => ApplicationServices.DbExec(db => db.GetIdentityUserRolesById(session.Id)));

    }
},

【讨论】:

  • 啊,好优雅。我忘记了 CacheClient 上的 GetOrCreate 方法。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-08-08
  • 1970-01-01
  • 2015-05-28
相关资源
最近更新 更多