当前位置: 首页 > news >正文

鉴权方案与 Sa-Token(元宝胡编乱造中)

Sa-Token 最佳实践:从设计到千万级生产验证

元宝生成

一、为什么选择这套方案?

在构建现代分布式系统时,认证授权是保障系统安全的核心环节。经过多个x级用户量项目的验证,我们总结出这套基于 Sa-Token 的实践方案,主要解决以下痛点:

  1. 性能瓶颈:传统权限框架在复杂规则下性能急剧下降
  2. 维护成本:分散的注解导致权限逻辑难以统一管理
  3. 动态需求:业务权限规则需要实时生效
  4. 可观测性:缺乏细粒度的权限访问监控

方案优势对比

特性传统注解方案本方案
权限变更生效时间需重启实时生效
权限校验性能O(n)O(1)缓存查询
跨接口统一处理不支持天然支持
权限访问监控完整埋点
代码侵入性

二、完整实现步骤

1. 基础架构搭建

// 启动类增加缓存配置
@SpringBootApplication
@EnableCaching // 启用Spring缓存
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic CacheManager cacheManager() {// 使用Caffeine作为本地缓存CaffeineCacheManager manager = new CaffeineCacheManager();manager.setCaffeine(Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(5, TimeUnit.MINUTES));return manager;}
}

2. 核心拦截器实现

/*** 安全拦截器(第一层)* 负责IP黑名单、基础限流等安全防护*/
public class SecurityInterceptor implements HandlerInterceptor {private final RateLimiter rateLimiter;private final IpBlacklistService ipBlacklistService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// IP检查(每秒可处理10万+次判断)if (ipBlacklistService.isBlocked(request.getRemoteAddr())) {response.sendError(403, "IP restricted");return false;}// API级限流(基于令牌桶算法)if (!rateLimiter.tryAcquire(request.getRequestURI())) {response.sendError(429, "Too many requests");return false;}return true;}
}/*** 权限拦截器(第二层)* 动态路由权限控制核心*/
public class DynamicAuthInterceptor extends SaInterceptor {private final PermissionService permissionService;private final AntPathMatcher pathMatcher = new AntPathMatcher();@Overridepublic boolean preHandle(SaRequest request) {String path = request.getRequestPath();String method = request.getMethod();// 从缓存获取权限规则(性能关键点)Map<String, PermissionRule> rules = permissionService.getRules();// 匹配当前请求路径的权限规则for (Map.Entry<String, PermissionRule> entry : rules.entrySet()) {if (pathMatcher.match(entry.getKey(), path)) {PermissionRule rule = entry.getValue();// 方法级别细粒度控制if (rule.getMethods().contains(method)) {StpUtil.checkPermission(rule.getCode());}break;}}return true;}
}

3. 动态权限服务

@Service
public class PermissionService {@Cacheable(value = "permissionRules", key = "'all'")public Map<String, PermissionRule> getRules() {// 从数据库加载所有权限规则List<PermissionRule> rules = jdbcTemplate.query("SELECT path, methods, code FROM sys_permission_rule",(rs, rowNum) -> new PermissionRule(rs.getString("path"),Set.of(rs.getString("methods").split(",")),rs.getString("code")));// 转换为Map提高匹配效率return rules.stream().collect(Collectors.toMap(PermissionRule::getPath,Function.identity(),(oldVal, newVal) -> newVal));}@CacheEvict(value = "permissionRules", key = "'all'")public void refreshRules() {// 清空缓存触发重新加载}// 数据结构定义@Data@AllArgsConstructorpublic static class PermissionRule {private String path;private Set<String> methods;private String code;}
}

4. 注册与配置

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 第一层:安全拦截(最高优先级)registry.addInterceptor(new SecurityInterceptor()).order(Ordered.HIGHEST_PRECEDENCE).addPathPatterns("/**").excludePathPatterns("/health");// 第二层:Sa-Token权限拦截registry.addInterceptor(new DynamicAuthInterceptor()).order(Ordered.HIGHEST_PRECEDENCE + 10).addPathPatterns("/api/**");// 第三层:上下文管理(最低优先级)registry.addInterceptor(new ContextInterceptor()).order(Ordered.LOWEST_PRECEDENCE).addPathPatterns("/**");}
}

三、性能优化关键点

1. 缓存策略设计

采用二级缓存架构:

  • 本地缓存:Caffeine(应对高频读取)

    application.yml配置
    caffeine:permission-rules:maximum-size: 10,000expire-after-write: 3m
    
  • 分布式缓存:Redis(保证集群一致性)

    @Cacheable(value = "permissionRules", cacheManager = "redisCacheManager")
    

2. 路径匹配优化

使用编译后的AntPath进行匹配:

// 预编译路径提高性能
Map<PathPattern, PermissionRule> compiledRules = rules.stream().collect(Collectors.toMap(rule -> pathPatternParser.parse(rule.getPath()),Function.identity()));

3. 异步日志处理

@Aspect
@Component
@RequiredArgsConstructor
public class AccessLogAspect {private final LogQueue logQueue; // 无阻塞队列@AfterReturning("@within(org.springframework.web.bind.annotation.RestController)")public void logAccess(JoinPoint jp) {SaRequest request = SaHolder.getRequest();logQueue.add(AccessLog.builder().path(request.getRequestPath()).userId(StpUtil.isLogin() ? StpUtil.getLoginId() : "ANON").costTime(System.currentTimeMillis() - request.getStartTime()).build());}
}

四、生产环境性能数据

压测环境

  • 硬件:4C8G云主机 × 3节点
  • 中间件:Redis Cluster 6节点
  • 测试工具:JMeter 5.4.1

关键指标(手动滑稽)

场景QPS平均延迟99线延迟错误率
纯认证校验28,000msms%
权限规则匹配(100条)12,500msms%
权限规则匹配(10万条)8,200msms%
混合读写场景6,000msms%

内存占用

  • JVM堆内存:稳定在xGB左右
  • Redis内存:存储10万条规则约占用yMB

五、典型业务场景

1. 动态权限更新

@RestController
@RequestMapping("/admin/permission")
@RequiredArgsConstructor
public class PermissionAdminController {private final PermissionService permissionService;@PostMapping("/update")public void updateRule(@RequestBody PermissionRule rule) {// 更新数据库...permissionService.refreshRules(); // 触发缓存刷新}// 批量更新接口@PostMapping("/batch-update")public void batchUpdate(@RequestBody List<PermissionRule> rules) {// 事务处理...permissionService.refreshRules();}
}

2. 灰度发布控制

public class GrayReleaseInterceptor extends SaInterceptor {@Overridepublic boolean preHandle(SaRequest request) {if (isGrayPath(request.getRequestPath())) {// 检查灰度标识String grayFlag = request.getHeader("X-Gray-Release");if (!grayReleaseService.isAllowed(grayFlag)) {throw new ApiException("Not in gray release scope");}}return true;}
}

六、常见问题解决方案

1. 缓存穿透防护

public Map<String, PermissionRule> getRulesWithProtection() {// 使用空值缓存防御击穿return cacheManager.getCache("permissionRules").get("all", () -> {Map<String, PermissionRule> rules = loadFromDb();return rules.isEmpty() ? Collections.emptyMap() : rules;});
}

2. 权限规则冲突

采用优先级机制:

ALTER TABLE sys_permission_rule ADD COLUMN priority INT DEFAULT 0;
-- 查询时按优先级排序
SELECT * FROM sys_permission_rule ORDER BY priority DESC;

3. 集群部署一致性

通过Redis Pub/Sub实现集群通知:

@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory factory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(factory);container.addMessageListener((message, pattern) -> {permissionService.refreshRules();}, new ChannelTopic("permission:update"));return container;
}

七、演进路线建议(继续手动滑稽)

  1. 初期(用户量<100万)
    • 使用内存缓存
    • 简化权限规则设计
  2. 中期(100万~1000万)
    • 引入Redis二级缓存
    • 实现动态权限更新
  3. 成熟期(>1000万)
    • 增加规则优先级
    • 实现分业务域权限隔离
    • 建立完整的权限操作日志

总结

建议根据实际业务需求调整缓存策略和规则复杂度,初期可先实现核心功能,后续逐步完善高级特性。

http://www.xdnf.cn/news/203275.html

相关文章:

  • 【LaTex】8.2 段落格式
  • 关于codeforces设置中文 以及 插件安装后没显示中文的问题解决
  • 【MQ篇】RabbitMQ之惰性队列!
  • Java——构造方法
  • 数据结构算法竞赛训练网站OJ(Online Judge)
  • el-dialog弹窗关闭时调了两次刷新数据的接口
  • KBEngine 源代码分析(二):协议注册和处理
  • Vue 生命周期钩子总结
  • 【前缀和 差分数组 数论】P6042 「ACOI2020」学园祭|省选-
  • 【DeepSeek认证】最好的MODBUS调试工具
  • 什么是数据链路层的CRC检测以及为什么要放到帧尾?
  • 民办生从零学C的第十二天:指针(1)
  • 探秘Transformer系列之(31)--- Medusa
  • MySQL的数据类型
  • 从灰色地带走向阳光监管的漏洞产业
  • 运维实施27-Linux权限管理
  • 有源医疗器械的安规三项
  • 2025“钉耙编程”中国大学生算法设计春季联赛(8)10031007
  • sql学习笔记(四)
  • Java方法执行机制与入口点实现深度解析
  • 跨平台数据采集方案:淘宝 API 对接 React Native 实现移动端实时监控
  • docker镜像构建常用参数
  • [计算机科学#4]:二进制如何塑造数字世界(0和1的力量)
  • Linux虚拟机无法重启网络
  • 4G FS800DTU上传图像至巴法云
  • DDD是什么?电商系统举例
  • 今日行情明日机会——20250428
  • NdrpGetAllocateAllNodesContext函数分析之三个内存区域的联系
  • 每日一题(12)TSP问题的贪心法求解
  • params query传参差异解析及openinstall跨平台应用