1. Sa-Token鉴权机制概述
Sa-Token作为一款轻量级Java权限认证框架,在Spring Boot生态中广泛用于接口权限控制。其核心工作原理是通过拦截器对请求进行鉴权校验,当请求到达控制器前,会经过如下处理流程:
- 请求到达Servlet容器
- 经过Sa-Token的拦截器层
- 执行
StpUtil.checkLogin()等鉴权方法 - 通过后进入业务控制器
- 返回响应数据
这种设计模式虽然保证了安全性,但在实际业务中常会遇到需要开放部分接口的场景。比如登录接口本身就不应该要求认证,公开的API文档接口也需要绕过鉴权。
1.1 鉴权拦截的核心实现
Sa-Token的鉴权拦截主要通过SaInterceptor实现,其核心逻辑如下:
java复制public class SaInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 执行路由拦截校验
SaRouter.match().check(r -> {
// 各种鉴权逻辑
StpUtil.checkLogin();
// 其他权限校验...
});
}
}
这种设计提供了灵活的匹配规则,也为后续的忽略鉴权方案奠定了基础。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种忽略鉴权方案详解
2.1 注解方式排除路径
最直观的方式是使用@SaCheckIgnore注解,这是Sa-Token提供的原生解决方案:
java复制@SaCheckIgnore
@GetMapping("/public/api")
public String publicApi() {
return "无需鉴权的接口";
}
实现原理:
- Sa-Token在初始化时会扫描所有带有
@SaCheckIgnore的控制器方法 - 将这些方法路径加入白名单列表
- 拦截器遇到白名单路径时直接放行
适用场景:
- 固定的公开接口
- 代码可控的内部系统
- 需要明确标识的免鉴权接口
注意事项:
- 注解只能用于方法级别,不能用于类级别
- 路径变更时需要同步修改注解
- 不适合动态配置的场景
2.2 配置式路径排除
在application.yml中配置排除路径:
yaml复制sa-token:
ignore:
- /public/**
- /docs/**
- /api/v1/open/**
底层机制:
- 框架启动时读取配置
- 将配置路径注册到
SaPathMatcher - 拦截器优先检查路径是否匹配排除规则
优势对比:
| 特性 | 注解方式 | 配置方式 |
|---|---|---|
| 修改是否需重启 | 是 | 是 |
| 集中管理 | 否 | 是 |
| 支持通配符 | 否 | 是 |
最佳实践:
- 将所有的开放接口路径统一前缀如
/open/ - 使用Ant风格路径表达式提高灵活性
- 生产环境建议配合配置中心实现动态刷新
2.3 动态鉴权逻辑控制
通过自定义拦截器实现更灵活的控制:
java复制public class CustomSaInterceptor extends SaInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 动态判断是否跳过鉴权
if(shouldIgnore(request)) {
return true;
}
return super.preHandle(request, response, handler);
}
private boolean shouldIgnore(HttpServletRequest request) {
// 实现动态逻辑:IP白名单、请求头校验等
}
}
典型应用场景:
- 基于请求参数的动态放行
- IP白名单系统
- 特殊Header校验放行
- 灰度发布时的特殊处理
性能考量:
- 每次请求都会执行判断逻辑
- 复杂逻辑可能影响吞吐量
- 建议配合缓存使用
3. 方案选型与实战建议
3.1 不同方案的对比分析
从三个维度评估各方案:
维护成本:
- 注解方式:路径变更需修改代码
- 配置方式:集中管理但需重启
- 动态方式:维护判断逻辑
灵活性:
- 注解方式:★☆☆☆☆
- 配置方式:★★★☆☆
- 动态方式:★★★★★
性能影响:
- 注解方式:无额外开销
- 配置方式:路径匹配轻微开销
- 动态方式:取决于逻辑复杂度
3.2 混合使用的最佳实践
在实际项目中推荐组合方案:
- 基础开放路径使用配置方式管理
yaml复制# application.yml
sa-token:
ignore:
- /swagger-ui/**
- /v3/api-docs
- 业务开放接口使用注解明确标识
java复制@SaCheckIgnore
@GetMapping("/product/list")
public List<Product> getPublicProducts() {
//...
}
- 特殊场景通过动态逻辑处理
java复制if(request.getHeader("X-Internal-Access") != null) {
// 内部系统调用放行
}
3.3 常见问题排查指南
问题1:配置了忽略路径但仍被拦截
- 检查路径是否完全匹配(包括大小写)
- 确认配置前缀是否正确(如
sa-token.ignore) - 查看是否有更高优先级的拦截器
问题2:动态放行后获取不到用户信息
java复制// 错误方式:直接放行会导致上下文为空
StpUtil.getLoginId();
// 正确做法:先放行但保留上下文
SaHolder.getStorage().set("ignoreAuth", true);
问题3:通配符不生效
- Ant风格路径规则:
?匹配单个字符*匹配0或多个字符**匹配0或多个目录
4. 高级应用场景扩展
4.1 基于条件的注解增强
结合Spring EL表达式实现智能判断:
java复制@SaCheckIgnore(condition = "#request.getHeader('from') == 'internal'")
@GetMapping("/hybrid/api")
public String hybridApi(HttpServletRequest request) {
//...
}
4.2 监控与审计处理
即使忽略鉴权也应记录访问日志:
java复制@Aspect
@Component
public class IgnoreAuthMonitor {
@AfterReturning("@annotation(SaCheckIgnore)")
public void logIgnoreAccess(JoinPoint jp) {
// 记录开放接口访问日志
}
}
4.3 安全加固措施
对于开放接口仍应做好防护:
- 请求频率限制
- 基础参数校验
- 敏感数据脱敏
- 基本的IP黑名单
java复制@SaCheckIgnore
@PostMapping("/public/submit")
@RateLimiter(value = 10) // 每秒10次限制
public Response publicApi(@Valid @RequestBody PublicDTO dto) {
//...
}
在项目实践中,我发现在网关层统一处理开放接口是更好的架构选择。具体做法是将Sa-Token的鉴权逻辑后移,在网关层先过滤掉明确不需要鉴权的请求,这样可以减轻业务服务的压力。同时建议建立完整的接口元数据管理系统,明确标识每个接口的安全等级和鉴权要求,这对后续的权限审计和安全加固都有很大帮助
