.NET后端减少数据库调用:角色与权限存储方案问询
基于.NET的微服务权限高效校验方案
针对5万并发用户、避免重复查库、不使用Session的需求,结合.NET生态提供以下几个落地性强的方案:
1. 将角色与权限嵌入JWT Token(推荐无状态场景)
Identity Server原生支持JWT格式令牌,直接把用户角色、权限作为Claims嵌入Token是最轻量化的无状态方案:
- 在Identity Server的
IProfileService实现类中,重写GetProfileDataAsync方法,从数据库查询用户角色和权限,添加到ProfileDataRequestContext.IssuedClaims集合。 - 下游微服务API通过
Microsoft.AspNetCore.Authentication.JwtBearer中间件验证Token时,直接从HttpContext.User.Claims中提取权限信息,无需额外调用数据库或存储服务。 - 注意:若权限粒度极细导致Token体积过大,可通过合并权限组(如用"order:manage"代替"order:create"+"order:edit"+"order:delete")控制Token大小,.NET的JWT解析性能完全能支撑5万并发。
示例代码片段:
// Identity Server侧自定义ProfileService public class CustomProfileService : IProfileService { private readonly IUserPermissionService _userPermissionService; public CustomProfileService(IUserPermissionService userPermissionService) { _userPermissionService = userPermissionService; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var userId = context.Subject.FindFirstValue(ClaimTypes.NameIdentifier); var userRoles = await _userPermissionService.GetUserRolesAsync(userId); var userPermissions = await _userPermissionService.GetUserPermissionsAsync(userId); var claims = new List<Claim>(); claims.AddRange(userRoles.Select(r => new Claim(ClaimTypes.Role, r))); claims.AddRange(userPermissions.Select(p => new Claim("permission", p))); context.IssuedClaims.AddRange(claims); } // 省略IsActiveAsync实现 }
2. 分布式缓存存储权限信息(推荐权限动态变更场景)
如果权限需要实时更新,或不想增大Token体积,可借助.NET的IDistributedCache(结合Redis实现分布式缓存)存储用户权限:
- 用户登录成功后,以Token值为Key,将角色、权限序列化为JSON存入Redis,缓存过期时间与Token过期时间保持一致。
- 下游API收到请求时,先解析Token获取缓存Key,从Redis读取权限数据;缓存命中直接校验,未命中则查库并更新缓存。
- .NET内置的
IDistributedCache可无缝集成Redis,5万并发用户的缓存压力Redis完全能承载,读写性能远高于数据库。 - 注意:用户权限变更时,需主动删除对应缓存Key,避免脏数据。
示例代码片段:
// API端权限校验逻辑 public async Task<bool> CheckUserPermissionAsync(HttpContext context, string requiredPermission) { var token = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); var cacheKey = $"user_perms:{token}"; var cachedPerms = await _distributedCache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedPerms)) { var permissions = JsonSerializer.Deserialize<List<string>>(cachedPerms); return permissions.Contains(requiredPermission); } // 缓存未命中,查库获取权限并更新缓存 var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var userPermissions = await _userPermissionService.GetUserPermissionsAsync(userId); await _distributedCache.SetStringAsync(cacheKey, JsonSerializer.Serialize(userPermissions), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }); return userPermissions.Contains(requiredPermission); }
3. 策略授权+本地缓存(优化高频API校验)
结合.NET的Policy-Based Authorization,在API端对用户权限校验结果做本地缓存:
- 定义自定义授权策略,在策略校验逻辑中用
IMemoryCache缓存用户对特定资源的权限判断结果,缓存时间可设为5-10分钟(根据权限变更频率调整)。 - 该方案适合高频调用的API,能减少重复校验开销,但仅适用于单实例场景;分布式环境下需配合分布式缓存使用,避免缓存不一致。
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JWT嵌入Claims | 无状态、无需额外存储、性能高 | Token体积可能增大、权限变更需重登 | 权限变更不频繁场景 |
| 分布式缓存存储权限 | 权限可动态更新、Token体积小 | 依赖缓存服务、需处理一致性 | 权限变更频繁场景 |
| 策略授权+本地缓存 | 高频API校验性能最优 | 分布式环境缓存不一致 | 单实例、高频API场景 |
另外,你之前考虑的TXT文件存储方案确实不适合大型项目:磁盘IO性能远低于内存缓存,并发场景下易出现文件锁、读写冲突,且无法横向扩展。
内容的提问来源于stack exchange,提问作者kmrraghav
相关产品推荐
相关产品推荐

