[Java实战]Spring Boot 3 整合 Apache Shiro(二十一)

[Java实战]Spring Boot 3 整合 Apache Shiro(二十一)

引言

在复杂的业务系统中,安全控制(认证、授权、加密)是核心需求。相比于 Spring Security 的重量级设计,Apache Shiro 凭借其简洁的 API 和灵活的扩展性,成为许多开发者的优选方案。本文将手把手演示如何在 Spring Boot 3 中整合 Shiro 1.12.0+,实现完整的权限管理功能。

一、环境准备

  • openJDK 17+(Spring Boot 3 强制要求)
  • Spring Boot 3.4.5
  • Apache Shiro 1.12.0(支持 Jakarta EE 9+)
  • Maven/Gradle(本文使用 Maven)
  • Redis(可选,用于会话管理)

二、项目依赖配置

pom.xml 中添加关键依赖:

xml 复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Shiro Core -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.12.0</version>
            <classifier>jakarta</classifier>
        </dependency>
        <!-- Servlet API (兼容性) -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>

        </dependency>
</dependencies>

三、核心组件配置

1. Shiro 配置类

创建 ShiroConfig.java 定义安全规则:

java 复制代码
@Configuration
public class ShiroConfig {

    // 注入自定义 Realm
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    // 配置 SecurityManager
    @Bean
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    // 配置 Shiro 过滤器
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        chain.addPathDefinition("/login", "anon");  // 匿名访问
        chain.addPathDefinition("/logout", "logout"); // 退出登录
        chain.addPathDefinition("/**", "authc"); // 需要认证
        return chain;
    }

    // 记住我功能
    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }
}

2. 自定义 Realm 实现

创建 UserRealm.java 实现认证与授权逻辑:

java 复制代码
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();
        
        // 查询用户角色和权限
        User user = userService.findByUsername(username);
        info.setRoles(user.getRoles());
        info.setStringPermissions(user.getPermissions());
        
        return info;
    }

    // 认证逻辑
    @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("用户不存在");
        }
        
        return new SimpleAuthenticationInfo(
            username, 
            user.getPassword(), 
            ByteSource.Util.bytes(user.getSalt()),
            getName()
        );
    }
}

四、权限控制实战

1. 控制器层注解控制

在 Controller 方法上使用 Shiro 注解:

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    @RequiresRoles("admin")  // 需要admin角色
    @GetMapping("/list")
    public ResponseEntity<List<User>> listUsers() {
        // 业务逻辑
    }

    @RequiresPermissions("user:delete") // 需要删除权限
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        // 业务逻辑
    }
}

2. 统一异常处理

通过 @ControllerAdvice 捕获 Shiro 异常:

java 复制代码
@Slf4j
@ControllerAdvice
public class ShiroExceptionHandler {

    @ExceptionHandler(AuthorizationException.class)
    public ResponseEntity<ApiResponse<?>> handleAuthError(AuthorizationException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(new ApiResponse<>(403, "权限不足"));
    }

    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<ApiResponse<?>> handleLoginError(AuthenticationException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(new ApiResponse<>(401, "认证失败"));
    }
}

五、高级功能扩展

1. 集成 Redis 会话管理

java 复制代码
@Bean
public SessionDAO sessionDAO() {
    RedisSessionDAO sessionDAO = new RedisSessionDAO();
    sessionDAO.setRedisManager(redisManager());
    return sessionDAO;
}

@Bean
public RedisManager redisManager() {
    RedisManager manager = new RedisManager();
    manager.setHost("localhost:6379");
    manager.setDatabase(0);
    return manager;
}

2. 密码加密配置

java 复制代码
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("SHA-256");
    matcher.setHashIterations(1024);
    matcher.setStoredCredentialsHexEncoded(false);
    return matcher;
}

// 在 Realm 中设置
userRealm.setCredentialsMatcher(hashedCredentialsMatcher());

六、常见问题排查

1. 权限注解不生效

  • 检查是否开启 AOP 支持:在配置类添加 @EnableAspectJAutoProxy
  • 确认方法为 public 且通过代理对象调用

2. 会话失效异常

  • 检查 Redis 连接配置
  • 确保 SessionDAO 实现已正确注入

七、性能优化建议

  1. 缓存授权信息 :使用 CachingRealm 减少数据库查询
  2. 限制会话数量 :配置 sessionManager 的全局会话上限
  3. 启用集群模式:通过 Redis 实现分布式会话

结语

通过本文,您已完成 Spring Boot 3 与 Apache Shiro 的深度整合。相比 Spring Security,Shiro 的配置更简洁,适合中小型项目快速实现安全控制。建议根据实际业务需求调整认证策略和缓存机制。

扩展阅读Shiro官方文档

希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!

相关推荐
枣伊吕波2 分钟前
第六节第二部分:抽象类的应用-模板方法设计模式
android·java·设计模式
xinxiyinhe5 分钟前
内存泄漏与OOM崩溃根治方案:JVM与原生内存池差异化排查手册
java·开发语言·jvm
心向阳光的天域9 分钟前
黑马Java跟学.最新AI+若依框架项目开发(一)
java
what_201814 分钟前
分布式链路跟踪
java·运维·分布式
oliveira-time22 分钟前
ArrayList和LinkedList区别
java·开发语言
潮流coder25 分钟前
IntelliJ IDEA给Controller、Service、Mapper不同文件设置不同的文件头注释模板、Velocity模板引擎
java·ide·intellij-idea
码农飞哥32 分钟前
互联网大厂Java求职面试实战:Spring Boot与微服务场景深度解析
java·数据库·spring boot·安全·微服务·消息队列·互联网医疗
Akiiiira1 小时前
【日撸 Java 300行】Day 14(栈)
java·开发语言
猴子请来的逗比4891 小时前
tomcat与nginx之间实现多级代理
java·nginx·tomcat
一丝晨光1 小时前
数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?
java·javascript·c++·rust·go·c·swift