Spring Security 与 Apache Shiro 两大安全框架比较

1. Spring Security 简介与基础

1.1 Spring Security 概述

Spring Security 是 Spring 官方提供的安全框架,功能全面,与 Spring 生态无缝集成。

1.2 快速开始

添加依赖

XML 复制代码
<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

基础配置类

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/")
                .permitAll()
            );
        return http.build();
    }
    
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        
        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin")
            .roles("ADMIN", "USER")
            .build();
            
        return new InMemoryUserDetailsManager(user, admin);
    }
}

2. Spring Security 核心配置

2.1 数据库用户认证

java 复制代码
@Service
public class CustomUserDetailsService implements UserDetailsService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) 
            throws UsernameNotFoundException {
        UserEntity user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
        
        return org.springframework.security.core.userdetails.User
            .withUsername(user.getUsername())
            .password(user.getPassword())
            .authorities(getAuthorities(user.getRoles()))
            .accountExpired(false)
            .accountLocked(false)
            .credentialsExpired(false)
            .disabled(false)
            .build();
    }
    
    private Collection<? extends GrantedAuthority> getAuthorities(
            Set<Role> roles) {
        return roles.stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
            .collect(Collectors.toList());
    }
}

2.2 JWT 集成

java 复制代码
@Component
public class JwtTokenProvider {
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    public String generateToken(Authentication authentication) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);
        
        return Jwts.builder()
            .setSubject(authentication.getName())
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
    }
    
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody();
        return claims.getSubject();
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

@Configuration
@EnableWebSecurity
public class JwtSecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter(), 
                UsernamePasswordAuthenticationFilter.class)
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            );
        return http.build();
    }
}

2.3 方法级安全控制

java 复制代码
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }
}

@Service
public class ProductService {
    
    @PreAuthorize("hasRole('ADMIN') or hasPermission(#productId, 'Product', 'READ')")
    public Product getProduct(Long productId) {
        // 方法实现
    }
    
    @PostAuthorize("returnObject.owner == authentication.name")
    public Product getProductDetails(Long id) {
        // 方法实现
    }
    
    @PreFilter("filterObject.owner == authentication.name")
    public void deleteProducts(List<Product> products) {
        // 方法实现
    }
}

3. Shiro 简介与基础

3.1 Shiro 概述

Apache Shiro 是一个功能强大且易于使用的 Java 安全框架,相对轻量级。

3.2 快速开始

添加依赖

XML 复制代码
<!-- Maven -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.9.1</version>
</dependency>

基础配置

java 复制代码
@Configuration
public class ShiroConfig {
    
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(
            SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        
        // 登录页面
        shiroFilter.setLoginUrl("/login");
        // 登录成功后的页面
        shiroFilter.setSuccessUrl("/index");
        // 未授权页面
        shiroFilter.setUnauthorizedUrl("/unauthorized");
        
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 静态资源允许匿名访问
        filterChainDefinitionMap.put("/static/**", "anon");
        // 登录页面允许匿名访问
        filterChainDefinitionMap.put("/login", "anon");
        // 需要认证
        filterChainDefinitionMap.put("/**", "authc");
        
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }
    
    @Bean
    public SecurityManager securityManager(Realm realm) {
        DefaultWebSecurityManager securityManager = 
            new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }
    
    @Bean
    public Realm realm() {
        return new CustomRealm();
    }
}

4. Shiro 核心配置

4.1 自定义 Realm

java 复制代码
public class CustomRealm extends AuthorizingRealm {
    
    @Autowired
    private UserService userService;
    
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        
        // 添加角色
        Set<String> roles = userService.getRolesByUsername(username);
        authorizationInfo.setRoles(roles);
        
        // 添加权限
        Set<String> permissions = userService.getPermissionsByUsername(username);
        authorizationInfo.setStringPermissions(permissions);
        
        return authorizationInfo;
    }
    
    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UnknownAccountException("用户不存在");
        }
        
        if (user.getLocked()) {
            throw new LockedAccountException("账户已锁定");
        }
        
        return new SimpleAuthenticationInfo(
            user.getUsername(),
            user.getPassword(),
            getName()
        );
    }
}

4.2 数据库集成

java 复制代码
@Configuration
public class ShiroDatabaseConfig {
    
    @Bean
    public Realm jdbcRealm(DataSource dataSource) {
        JdbcRealm realm = new JdbcRealm();
        realm.setDataSource(dataSource);
        realm.setAuthenticationQuery(
            "SELECT password, salt FROM users WHERE username = ?");
        realm.setUserRolesQuery(
            "SELECT role_name FROM user_roles WHERE username = ?");
        realm.setPermissionsQuery(
            "SELECT permission FROM roles_permissions WHERE role_name = ?");
        realm.setPermissionsLookupEnabled(true);
        realm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN);
        return realm;
    }
}

4.3 缓存配置

java 复制代码
@Configuration
public class ShiroCacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }
    
    @Bean
    public SecurityManager securityManager(Realm realm, CacheManager cacheManager) {
        DefaultWebSecurityManager securityManager = 
            new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setCacheManager(cacheManager);
        return securityManager;
    }
}

4.4 会话管理

java 复制代码
@Configuration
public class ShiroSessionConfig {
    
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(1800000); // 30分钟
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
}

5. 两大框架对比与选择

5.1 功能对比

特性 Spring Security Apache Shiro
与Spring集成 原生支持,无缝集成 需要额外配置
学习曲线 较陡峭 相对平缓
功能完整性 非常完整 基本功能齐全
配置方式 注解/Java配置 INI/Java配置
社区支持 Spring官方,活跃 Apache,活跃
微服务支持 优秀(Spring Cloud Security) 需要自行扩展
OAuth2 原生支持 需要扩展

5.2 选择建议

选择 Spring Security 当:

  • 项目基于完整的 Spring 生态

  • 需要 OAuth2、JWT 等现代安全协议

  • 项目规模较大,需要细粒度的权限控制

  • 使用 Spring Cloud 微服务架构

选择 Shiro 当:

  • 项目相对简单,不需要复杂的权限控制

  • 团队对 Shiro 更熟悉

  • 需要更轻量级的安全解决方案

  • 项目不是基于 Spring 全家桶

5.3 实际应用示例

Spring Security + JWT 的微服务安全架构:

XML 复制代码
# application.yml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://auth-server:9000
          jwk-set-uri: http://auth-server:9000/.well-known/jwks.json

jwt:
  secret: your-jwt-secret-key-here
  expiration: 86400000

Shiro 多 Realm 配置示例:

java 复制代码
@Bean
public ModularRealmAuthenticator authenticator() {
    ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
    authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
    return authenticator;
}

@Bean
public SecurityManager securityManager(List<Realm> realms) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealms(realms);
    securityManager.setAuthenticator(authenticator());
    return securityManager;
}

5.4 最佳实践

  1. 最小权限原则:只授予必要的最小权限

  2. 密码安全:使用 BCrypt 等强哈希算法

  3. 会话管理:合理设置会话超时时间

  4. CSRF 防护:确保启用 CSRF 保护

  5. 安全审计:记录重要的安全事件

  6. 定期更新:保持安全框架版本更新

  7. 安全测试:定期进行安全漏洞扫描


总结

Spring Security 和 Apache Shiro 都是优秀的 Java 安全框架。Spring Security 更适合 Spring 生态的项目,功能全面但学习曲线较陡;Shiro 更轻量级,配置简单,适合中小型项目或非 Spring 项目。选择哪个框架应根据项目需求、团队技术栈和技术熟悉度来决定。

无论选择哪个框架,都要遵循安全最佳实践,确保应用程序的安全性。

相关推荐
岱宗夫up1 分钟前
基于ROS的视觉导航系统实战:黑线循迹+激光笔跟随双模态实现(冰达机器人Nano改造)
linux·python·机器人·ros
Y_0320 分钟前
SpringBoot+VUE3的图书管理系统
vue.js·spring boot·毕业设计·数据可视化
别或许37 分钟前
python中的异步调用(直接使用教程)
java·前端·python
上海云盾第一敬业销售38 分钟前
构建坚不可摧的网站安全防护策略
安全·web安全·ddos
百***243738 分钟前
DeepSeek-V3.2全解析:开源大模型的性能巅峰与落地实践指南
python·开源
像少年啦飞驰点、44 分钟前
零基础入门 Spring Boot:从‘Hello World’到可部署微服务的完整学习路径
java·spring boot·web开发·编程入门·后端教程
方安乐1 小时前
杂记:Quart和Flask比较
后端·python·flask
嫂子开门我是_我哥1 小时前
第十六节:异常处理:让程序在报错中稳定运行
开发语言·python
测试19981 小时前
如何使用Appium实现移动端UI自动化测试?
自动化测试·软件测试·python·测试工具·ui·appium·测试用例
yuankoudaodaokou1 小时前
无图纸如何定制汽车外饰件?3DeVOK MT+ QUICKSURFACE逆向设计解决方案
python·3d·汽车·机器翻译