如何使用In Memory Caching优化用户权限授权性能
用内存缓存优化权限校验性能
改动步骤
注入内存缓存服务
在UserPolicy类中引入IMemoryCache,用于管理内存中的权限数据。更新构造函数
修改构造函数,接收注入的IMemoryCache实例。实现缓存逻辑
权限校验时优先从缓存读取用户权限列表;缓存未命中时,从数据库加载数据并写入缓存,后续请求直接复用缓存数据完成校验。
修改后的完整代码
private readonly IHttpContextAccessor _contextAccessor; private readonly ApplicationDbContext _dbContext; private readonly IMemoryCache _memoryCache; public UserPolicy(IHttpContextAccessor contextAccessor, ApplicationDbContext dbContext, IMemoryCache memoryCache) { _contextAccessor = contextAccessor; _dbContext = dbContext; _memoryCache = memoryCache; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPolicyRequirement requirement) { var request = _contextAccessor.HttpContext?.Request; if (request == null) { context.Fail(); return Task.CompletedTask; } string actionName = request.RouteValues["action"].ToString(); string controllerName = request.RouteValues["controller"].ToString(); var token = request.Headers[HeaderNames.Authorization].ToString(); if (!string.IsNullOrEmpty(token)) { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token.Replace("Bearer ", "")) as JwtSecurityToken; if (jwtToken == null) { context.Fail(); return Task.CompletedTask; } var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == "userId")?.Value; if (string.IsNullOrEmpty(userId)) { context.Fail(); return Task.CompletedTask; } // 定义缓存键,确保每个用户的权限独立存储 var cacheKey = $"UserPermissions_{userId}"; // 尝试从缓存获取权限列表,不存在则从数据库加载 if (!_memoryCache.TryGetValue(cacheKey, out List<UserAccessAction> userPermissions)) { // 从数据库查询用户所有权限 userPermissions = _dbContext.userAccessActions .Where(a => a.UserId == userId) .ToList(); // 设置缓存选项:滑动过期30分钟(无访问自动清理)+ 绝对过期2小时(定期刷新) var cacheOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(30)) .SetAbsoluteExpiration(TimeSpan.FromHours(2)); // 将权限列表写入缓存 _memoryCache.Set(cacheKey, userPermissions, cacheOptions); } // 用缓存中的权限列表完成校验 if (userPermissions.Any(a => a.ActionName == actionName && a.ControllName == controllerName)) { // 用户拥有当前接口权限 context.Succeed(requirement); } else { // 用户无当前接口权限 context.Fail(); } } else { // 未携带token,直接拒绝访问 context.Fail(); } return Task.CompletedTask; }
关键说明
- 缓存键设计:以
UserPermissions_{userId}作为缓存键,避免不同用户的权限数据互相干扰。 - 过期策略:同时配置滑动过期和绝对过期,既清理长期不活跃用户的缓存,也定期刷新缓存,避免权限变更后数据不一致。
- 空值防护:新增对
request、jwtToken、userId的空值判断,防止空引用异常。
内容的提问来源于stack exchange,提问作者sadegh
相关产品推荐
相关产品推荐

