Featured image of post Java 权限框架怎么选——Shiro 与 Spring Security 对比

Java 权限框架怎么选——Shiro 与 Spring Security 对比

国内项目最常用的两套 Java 权限框架——一个轻量灵活,一个体系庞大。本文从设计哲学、核心能力、扩展难度全面对比

一句话先把定位讲清楚

ShiroSpring Security
起源Apache,2009Spring 全家桶原生
设计哲学简单、上手快、最小依赖完备、可扩展、深度集成
体系庞大度轻量重量
默认特性认证 + 授权 + Session + 加密认证 + 授权 + 大量协议(OAuth2、CSRF、CORS、JWT)
学习曲线平缓陡峭
与 Spring 集成适配良好但不"自家"原生

简单说:

Shiro 是『来即用的工具』,Spring Security 是『全功能的框架』

下面把这两套方案的差异、各自优劣、不同场景下的选型建议讲清楚。


一、概念结构对比

Shiro 的核心概念

  • Subject:当前操作的用户主体
  • SecurityManager:所有功能的入口
  • Realm:连接用户/角色/权限数据源(DB、LDAP、缓存)

代码风格非常直观:

1
2
3
4
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username, password));
subject.checkRole("admin");
subject.checkPermission("user:delete");

Spring Security 的核心概念

  • FilterChainProxy:所有请求都过的过滤器链
  • AuthenticationManager / Provider:认证流水线
  • UserDetailsService:连数据源
  • SecurityContext:认证后的上下文(线程级)
  • AuthorizationManager:授权决策

业务里典型用法:

1
2
3
4
5
6
7
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
    userService.delete(id);
}

// 或者细粒度
@PreAuthorize("hasAuthority('user:delete')")

一眼看上去 Spring Security 概念多得多——这是它的本质特征:用更细分的角色解耦每一步


二、特性能力对比

ShiroSpring Security
用户名密码登录
角色 / 权限模型✓ 简洁✓ 强(GrantedAuthority)
会话管理✓ 内置 Session✓ HttpSession + 自定义
记住我
加密工具
注解鉴权@RequiresPermissions@PreAuthorize
URL 鉴权✓ ini / Java DSL✓ Java DSL
OAuth2 / OIDC△ 第三方扩展原生
CSRF 防御✓ 默认开启
CORS
JWT△ 第三方✓ Resource Server / OAuth2 配套
多因子认证
Method 级注解 SpEL✓ 简单✓ 强大(SpEL 全支持)
Reactive 支持✗ 不支持✓ WebFlux 原生

差距的几个关键点:

  • OAuth2 / OIDC:只要你的系统涉及第三方登录、单点登录、服务对接、API 鉴权,Spring Security 几乎是唯一现实选择。Shiro 也能做,但要拼装大量代码
  • CSRF / CORS:Shiro 不管这些,要自己写过滤器;Spring Security 默认就帮你做好了
  • JWT:两者都能做,但 Spring Security 5.x 之后 Resource Server 支持极完善
  • Reactive:WebFlux 项目根本不要考虑 Shiro

三、典型代码对比

同样是"用户名密码登录 + 检查权限":

Shiro

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 配置 Realm
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) {
        String username = (String) p.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(userService.getRoles(username));
        info.setStringPermissions(userService.getPermissions(username));
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        UsernamePasswordToken upt = (UsernamePasswordToken) token;
        User user = userService.findByUsername(upt.getUsername());
        if (user == null) throw new UnknownAccountException();
        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
    }
}

// 业务里
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken("admin", "123456"));
subject.checkPermission("user:delete");

Spring Security

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// UserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userService.findByUsername(username);
        if (user == null) throw new UsernameNotFoundException(username);
        return org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(user.getAuthorities())
                .build();
    }
}

// SecurityFilterChain
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/login").permitAll()
            .anyRequest().authenticated())
        .formLogin(Customizer.withDefaults())
        .csrf(Customizer.withDefaults())
        .build();
}

// 业务里
@PreAuthorize("hasAuthority('user:delete')")
public void delete(Long id) { ... }

Shiro 的代码更直观——但每件事都要你做主动调用;Spring Security 是"声明 + 自动连接",刚开始绕,习惯了反而省心。


四、扩展性

Shiro:扩展点少而清晰

Shiro 把扩展点限定在几个核心接口:RealmSessionManagerAuthenticatorFilter。要做特殊认证(比如手机验证码登录)只需要自己写个 Token + 改 Realm扩展边界小、写起来直接

Spring Security:扩展点多但有学习曲线

Spring Security 有几十个可扩展点:AuthenticationProviderUserDetailsServiceSecurityContextRepositoryAuthenticationEntryPointAccessDeniedHandler、各种 Filter……第一次接它的人会被这么多概念吓到

但一旦熟悉了:

  • 想要"OAuth2 + 数据库角色 + JWT 接口 + 表单登录"四种方式共存?只是几个 SecurityFilterChain 的事
  • 想要把 Session 存进 Redis 实现集群登录?SecurityContextRepository 改一个就够
  • 想要某个 URL 用一种鉴权方式、另一些用另一种?多 SecurityFilterChain 按顺序匹配

复杂场景下 Spring Security 远胜 Shiro——因为它把每件事都拆成了独立的扩展点。


五、社区与维护活跃度

ShiroSpring Security
主线版本1.13.x(2024)6.x,与 Spring 6 同步
发布频率一年一两个小版本跟 Spring Boot 节奏同步迭代
中文资料较多(早期热门)越来越多
国际社区中等
招聘频率中小厂偶尔提及大厂、外企必考

国内 Shiro 用户多,是因为它早期太"友好"——五分钟上手,老项目就用了下来。但近年 Shiro 历史上爆出过多个 RCE 漏洞(CVE-2020-1957、CVE-2020-11989、CVE-2022-32532 等),这是用 Shiro 的项目必须持续关注版本升级的硬性事实。

Spring Security 漏洞同样有,但修复响应快、官方推送积极、版本管理跟着 Spring Boot 一起走——大型项目更省心。


六、性能差距:基本不存在

很多博客争论性能差距——这是个伪问题

  • Shiro 和 Spring Security 都是"内存中读权限规则、过滤器链拦截请求"
  • 性能瓶颈永远在你写的 Realm / UserDetailsService——也就是数据库查询
  • 单次请求的鉴权耗时通常在几百微秒到 1ms 之间,业务代码随便就比这慢一个数量级

不要拿微基准跑出来的数字当选型依据。


七、什么场景选什么

选 Shiro 的场景

  • 小到中型项目,权限模型简单(角色 + 几条权限)
  • 不涉及 OAuth / SSO / 第三方登录
  • 团队对 Shiro 已经熟悉、已有项目大量使用
  • 不要 Reactive / WebFlux

选 Spring Security 的场景

  • 需要 OAuth2、OIDC、SSO、JWT 等标准协议
  • 大型 / SaaS / 多租户系统
  • 有 WebFlux / Reactive 需求
  • 新项目、想跟 Spring 全家桶生态保持一致
  • 大厂面试 / 简历加分

八、共存与迁移建议

共存

老项目用 Shiro,新模块想用 Spring Security——理论上能共存,但强烈不推荐。两套过滤器链同时存在会很混乱,调试链路变长,维护成本极高。一个项目只用一套

老项目从 Shiro 迁移到 Spring Security

迁移成本不小,但路径相对固定:

  1. Realm → UserDetailsService + AuthenticationProvider
  2. @RequiresPermissions@PreAuthorize
  3. Filter 配置改写成 SecurityFilterChain
  4. Session 管理改 Spring Security 的 SecurityContext

要点:先打通主链路(登录 + 一个接口的鉴权),再批量替换注解——不要一口气全切。


九、几个被反复争论的话题

争论 1:Spring Security 太复杂

部分对——但它复杂的地方多数是"复杂功能不是你需要的功能"。基础的"账号密码登录 + 接口鉴权"配置极简:

1
2
3
4
5
6
7
@Bean
public SecurityFilterChain chain(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults())
        .build();
}

是不是看起来比 Shiro 还少?

争论 2:Shiro 文档少

部分对——Shiro 官方文档质量一般,社区资料质量参差。Spring Security 文档有官方系统手册 + 大量社区文章,反而更容易学。

争论 3:Sa-Token 是不是更好的选择?

Sa-Token 是国内开源的轻量框架,API 极简,适合中小项目快速上手。它的定位更接近 Shiro——简单、易用、适合中小项目。

但 Sa-Token 的社区影响力和 Spring Security 不在一个量级,团队对接外部系统、招聘新人、跟进安全更新时仍然有差距。有兴趣可以学,但不要在大型项目里盲目替换主流框架


十、决策树


小结

把全文压一句:

Shiro 上手快、能力够、维护需谨慎;Spring Security 学习陡、能力全、生态强。中小项目都行,中大型项目优先 Spring Security。

工程上几条建议:

  1. 新项目优先 Spring Security——尤其涉及 OAuth/SSO 时
  2. 老项目用 Shiro 稳定运行的,不要折腾——但版本升级要跟住 CVE
  3. 不要两套共存——选一个,全量做
  4. 学权限框架不只是学 API,要学概念——用得好的关键是理解 Authentication / Authorization / Filter Chain / Context 这些底层概念
  5. 不要把"网上博客对比性能差几毫秒"当选型依据——那是噪声

把这两套之中任意一套用熟,配合好你的业务权限模型,安全这层基本就稳住了。

使用 Hugo 构建
主题 StackJimmy 设计