ServiceStack 缓存用户角色和权限的方法
ServiceStack caching users roles and permissions approach
使用 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 本身(或为获取此信息而编写的任何存储库代码)?
我在 this commit. This change is available in the latest v5.4.1 pre-release that's now on MyGet.
中包含了对访问和缓存 ASP.NET 身份用户角色的更好支持
var userManager = req.TryResolve<UserManager<ApplicationUser>>();
var user = userManager.FindByIdAsync(session.Id).Result;
var roles = userManager.GetRolesAsync(user).Result;
虽然这有效,但它使用 "sync over async",这是不鼓励的,因为它在许多用例中存在问题,效率低于同步,并且有限的 API 强制多个数据库调用。
新的 IDbConnection.GetIdentityUserRolesById(userId)
API 现在是我们推荐的更高效的 API,它解决了上述问题并利用单个数据库调用仅获取 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 因为它保存在内存中,而是使用 Registered 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)));
}
},
使用 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 本身(或为获取此信息而编写的任何存储库代码)?
我在 this commit. This change is available in the latest v5.4.1 pre-release that's now on MyGet.
中包含了对访问和缓存 ASP.NET 身份用户角色的更好支持var userManager = req.TryResolve<UserManager<ApplicationUser>>();
var user = userManager.FindByIdAsync(session.Id).Result;
var roles = userManager.GetRolesAsync(user).Result;
虽然这有效,但它使用 "sync over async",这是不鼓励的,因为它在许多用例中存在问题,效率低于同步,并且有限的 API 强制多个数据库调用。
新的 IDbConnection.GetIdentityUserRolesById(userId)
API 现在是我们推荐的更高效的 API,它解决了上述问题并利用单个数据库调用仅获取 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 因为它保存在内存中,而是使用 Registered 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)));
}
},