ServiceStack缓存用户角色和权限的方法

时间:2019-02-06 13:40:16

标签: servicestack

使用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本身(或任何存储库)中实现缓存解决方案的情况编写代码以获取此信息)?

1 个答案:

答案 0 :(得分: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,可轻松利用您的App上下文数据库连接。

通过此方法,您可以使用更简洁高效的替代方法来获取用户角色:

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)));

    }
},