一、你还在为 Shiro 手动造轮子吗?
作为 Spring Boot 开发者,搭建项目安全层时,你是否遇到过这些场景:
- 想快速实现表单登录,却要为 Shiro 写一堆过滤器、配置跳转页面;
- 需要 "记住我" 功能,查了半天文档,发现 Shiro 得自己写 Cookie 存储、会话关联;
- 对接 OAuth2(比如微信登录),Shiro 要手动集成第三方依赖,而同事用 Spring Security 几行配置就搞定了;
- 上线前要做密码加密,Shiro 得自己写盐值处理,Security 早已内置强哈希算法自动适配。
其实,在 Spring Boot 生态中,Spring Security 不是 "备选",而是 "标配" ------ 它的 "开箱即用" 特性,恰恰击中了开发者最核心的需求:少写重复代码,多聚焦业务逻辑。而 Shiro 虽轻量,但在 Spring Boot 环境下,太多基础功能需要手动实现,反而成了 "效率绊脚石"。
二、这些 "默认实现",让 Security 甩 Shiro 几条街
1. 表单登录:Security 零配置,Shiro 再用过滤器也需手动折腾
作为 Web 应用的基础功能,表单登录的实现差异,直接体现框架的便捷性。很多人误以为 Shiro 的 FormAuthenticationFilter 能像 Security 一样 "一键生效",但实际使用中仍需多步手动配置:
(1)Shiro + FormAuthenticationFilter:需 3 步手动配置才生效
FormAuthenticationFilter 虽能替代 "手动写登录接口",但必须完成以下配置,少一步都无法工作:
java
@Configuration
public class ShiroConfig {
// 第一步:配置自定义Realm(认证逻辑仍需自己写)
@Bean
public Realm myRealm() {
SimpleAccountRealm realm = new SimpleAccountRealm();
realm.addAccount("admin", "123456", "ADMIN"); // 模拟用户(实际需从数据库查询)
return realm;
}
// 第二步:配置SecurityManager,注入Realm
@Bean
public SecurityManager securityManager(Realm myRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
return manager;
}
// 第三步:配置FormAuthenticationFilter,指定表单参数名、登录URL等
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
// 手动指定登录页(必须配置,否则过滤器不知道跳哪)
factory.setLoginUrl("/login");
// 手动指定登录成功页
factory.setSuccessUrl("/index");
// 手动指定登录失败页
factory.setUnauthorizedUrl("/login?error");
// 手动配置过滤器链:将FormAuthenticationFilter应用到登录请求
Map<String, String> filterMap = new HashMap<>();
filterMap.put("/login", "authc"); // 登录请求交给authc过滤器(即FormAuthenticationFilter)
filterMap.put("/**", "authc"); // 其余请求需认证
factory.setFilterChainDefinitionMap(filterMap);
// (可选)手动自定义表单参数名(默认是username/password,若前端参数名不同需配置)
Map<String, Filter> filters = new HashMap<>();
FormAuthenticationFilter authcFilter = new FormAuthenticationFilter();
authcFilter.setUsernameParam("userName"); // 手动指定用户名参数名
authcFilter.setPasswordParam("passWord"); // 手动指定密码参数名
filters.put("authc", authcFilter);
factory.setFilters(filters);
return factory;
}
}
- 关键注意点:
- 仍需手动写 Realm 的认证逻辑(从数据库查用户、校验密码);
- 必须手动指定
loginUrl、successUrl等路径,否则过滤器无法工作; - 若前端登录表单的参数名不是默认的
username/password,需手动配置参数映射; - 登录页面仍需自己开发(HTML/Thymeleaf 模板),Shiro 不会自动生成。
(2)对比 Spring Security:零配置 + 自动生成,无需手动干预
同样是表单登录,Spring Security 无需手动指定过滤器、无需手动配置参数名、甚至无需自己开发登录页:
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// 只需配置用户信息(实际项目对接数据库即可)
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 核心配置:一行开启表单登录,其余全自动化
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin(); // 仅这一行,自动实现所有功能
return http.build();
}
}
- Spring Security 自动帮你做了这些事(Shiro 需手动配置):
- 自动生成登录页面(含用户名 / 密码输入框、提交按钮),无需自己写 HTML;
- 自动处理登录请求(默认
/login接口),无需手动配置过滤器; - 自动适配表单参数名(默认
username/password,可通过formLogin().usernameParameter("userName")一行修改,无需手动创建过滤器对象); - 自动处理登录成功 / 失败的跳转、异常信息返回(如用户名不存在、密码错误);
- 自动集成密码加密,无需手动在 Realm 中处理。
(3)核心差异对比表
| 对比维度 | Shiro + FormAuthenticationFilter | Spring Security 表单登录 |
|---|---|---|
| 是否需要手动配置过滤器 | 是(需手动将 authc 过滤器绑定到登录路径) | 否(自动注册过滤器,无需手动配置) |
| 是否需要手动开发登录页 | 是(必须自己写 HTML / 模板) | 否(自动生成标准登录页,可直接使用) |
| 是否需要手动配置路径 | 是(必须指定 loginUrl、successUrl、unauthorizedUrl) | 否(默认路径 /login,可通过配置快速修改) |
| 是否需要手动处理参数映射 | 是(参数名不同时,需手动创建 FormAuthenticationFilter 对象) | 否(通过 formLogin().usernameParameter("userName") 一行修改) |
| 是否自动集成密码加密 | 否(需手动配置 HashedCredentialsMatcher) | 是(内置 PasswordEncoder,自动加密验证) |
2. 密码加密:Security 自动适配,Shiro 手动编码
密码明文存储是高危漏洞,而加密功能的实现门槛,Security 几乎为零:
- Spring Security :内置
PasswordEncoder接口,提供BCrypt、Pbkdf2等强哈希算法,自动处理密码加密与验证。只需定义密码编码器 Bean,用户创建时自动加密,登录时自动匹配 ------ 无需手动写一行加密代码:
java
// Security 密码加密零编码
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // 配置强哈希算法
}
// 创建用户时自动加密
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("admin")
.password(passwordEncoder().encode("123456")) // 自动加密
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
- Shiro :虽然支持加密,但需要手动实现
CredentialsMatcher接口,或在 Realm 中手动处理盐值和哈希计算,步骤繁琐且容易出错:
java
// Shiro 需手动配置密码匹配器
@Bean
public HashedCredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("MD5"); // 手动指定算法
matcher.setHashIterations(1024); // 手动指定迭代次数
return matcher;
}
// 在 Realm 中手动处理加密验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
String username = (String) token.getPrincipal();
String dbPassword = "加密后的密码"; // 从数据库查询
String salt = "用户盐值"; // 需手动存储盐值
// 手动封装认证信息,指定盐值
return new SimpleAuthenticationInfo(username, dbPassword, ByteSource.Util.bytes(salt), getName());
}
3. "记住我" 功能:Security 一键开启,Shiro 手动造轮子
"记住我" 看似简单,实则涉及 Cookie 存储、会话关联、安全校验,而 Security 直接一键支持:
- Spring Security :只需在配置中添加
rememberMe(),自动生成安全 Cookie、存储用户信息、有效期配置,甚至支持自定义 Cookie 名称和失效时间 ------ 零额外代码:
java
// Security 一键开启"记住我"
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.rememberMe()
.rememberMeParameter("remember-me") // 自定义参数名
.tokenValiditySeconds(86400) // 有效期1天,默认2周
.and().authorizeHttpRequests().anyRequest().authenticated();
return http.build();
}
- Shiro :需要手动配置
RememberMeManager、CookieManager,甚至要自己处理 Cookie 的创建、加密和解码,步骤繁琐且容易出现安全隐患:
java
// Shiro 手动配置"记住我"
@Bean
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager manager = new CookieRememberMeManager();
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setMaxAge(86400); // 手动设置有效期
manager.setCookie(cookie);
// 手动设置加密密钥(需自己保管,否则有安全风险)
manager.setCipherKey(Base64.decode("d3d3LnNoaXJvLmNvbS9jaGFyc2V0LTEyMzQ1Ng=="));
return manager;
}
// 还要在 SecurityManager 中手动注入
@Bean
public SecurityManager securityManager(Realm myRealm, CookieRememberMeManager rememberMeManager) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
manager.setRememberMeManager(rememberMeManager); // 手动关联
return manager;
}
4. OAuth2 / 第三方登录:Security 内置支持,Shiro 手动集成
如今微服务、跨平台应用普遍需要 OAuth2(如微信、GitHub 登录),而这正是 Security 的 "强项":
- Spring Security :通过
spring-security-oauth2-client依赖,内置 OAuth2 客户端 / 资源服务器实现,对接微信、GitHub 等第三方登录只需配置客户端 ID、密钥和授权地址,无需手动处理令牌交互、用户信息获取:
yaml
# Spring Security 配置 OAuth2 微信登录(application.yml)
spring:
security:
oauth2:
client:
registration:
weixin:
client-id: 你的微信APPID
client-secret: 你的微信密钥
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/weixin
provider:
weixin:
authorization-uri: https://open.weixin.qq.com/connect/oauth2/authorize
token-uri: https://api.weixin.qq.com/sns/oauth2/access_token
user-info-uri: https://api.weixin.qq.com/sns/userinfo
- Shiro :本身不支持 OAuth2,需要手动集成
shiro-oauth2第三方依赖,自己实现 OAuth2 过滤器、令牌解析、用户信息映射,甚至要处理令牌刷新、过期等细节 ------ 相当于从零开发一套 OAuth2 集成方案,成本极高。
5. 方法级授权:Security 注解即生效,Shiro 需额外配置
细粒度权限控制(如 "只有管理员能删除用户"),Security 用注解就能实现:
- Spring Security :开启
@EnableMethodSecurity后,直接在方法上用@PreAuthorize配合 SpEL 表达式,支持角色、权限、数据级别的细粒度控制,无需额外配置过滤器:
java
@EnableMethodSecurity // 开启方法级授权
@RestController
public class UserController {
// 只有 ADMIN 角色且有 user:delete 权限才能调用
@PreAuthorize("hasRole('ADMIN') and hasPermission('user:delete')")
@DeleteMapping("/user/{id}")
public void deleteUser(@PathVariable Long id) {
// 业务逻辑
}
}
- Shiro :虽然支持
@RequiresRoles注解,但需要手动配置AuthorizationAttributeSourceAdvisor才能生效,且不支持 SpEL 表达式,细粒度控制需自定义Permission实现,灵活性和便捷性远不如 Security:
java
// Shiro 需手动配置注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
// 注解仅支持简单角色/权限判断,不支持复杂表达式
@RequiresRoles("ADMIN")
@DeleteMapping("/user/{id}")
public void deleteUser(@PathVariable Long id) {
// 业务逻辑
}
三、选型结论:Spring Boot 环境下,Security 是 "最优解" 而非 "备选项"
通过以上对比,我们不难发现:在 Spring Boot 生态中,Spring Security 的便捷性是 Shiro 无法替代的 ------ 它将开发者从重复的安全基础开发中解放出来,用 "默认实现" 覆盖 80% 的业务场景,而 Shiro 即便使用 FormAuthenticationFilter 这样的内置组件,仍需要为基础功能编写大量模板代码。
什么时候选 Shiro?
- 非 Spring 项目(如 Java SE 桌面应用);
- 对框架体积有极致要求(Shiro 核心包更小);
- 需要跨 Web / 非 Web 环境的统一会话管理。
什么时候必选 Spring Security?
- 所有 Spring Boot/Spring Cloud 项目;
- 需要快速上线、减少重复开发;
- 涉及 OAuth2/OpenID Connect、微服务安全、细粒度权限控制;
- 追求 "配置即生效" 的开发体验。
四、最后:技术选型的核心是 "匹配场景 + 提升效率"
Spring Security 并非完美,但在 Spring Boot 环境下,它与生态的深度整合、丰富的默认实现、强大的扩展能力,恰恰解决了开发者最核心的痛点 ------少写代码、少踩坑、多聚焦业务。
与其在 Shiro 中手动配置过滤器、开发登录页、处理加密逻辑,不如让 Spring Security 帮你 "一键搞定",把节省下来的时间用在业务创新上 ------ 这才是技术选型的真正价值。