深入解析:Spring Boot与Apache Shiro的无缝集成实践

1. 添加依赖

在Spring Boot项目中,依赖管理是通过pom.xml文件完成的。为了将Apache Shiro集成到项目中,我们需要引入Shiro的核心依赖以及Spring Boot的整合依赖。同时,为了支持Web功能,我们还需要引入Spring Boot Web模块的依赖。以下是完整的依赖配置:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- Apache Shiro Spring Boot Starter -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.10.0</version> <!-- 请根据项目需求选择合适的版本 -->
    </dependency>

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

这段配置为项目引入了Spring Boot的核心功能、Shiro的安全管理能力以及Web开发所需的支持。通过这种方式,我们可以确保项目在启动时自动加载Shiro的相关组件,并与Spring Boot的生命周期无缝集成。这不仅简化了配置过程,还提高了系统的整体性能和可维护性。


2. 配置Shiro

在Spring Boot中,配置类是通过@Configuration注解定义的。我们需要创建一个ShiroConfig类,用于配置Shiro的核心组件,包括ShiroFilterFactoryBeanDefaultWebSecurityManager。这些组件将负责管理Shiro的过滤器链、安全策略以及与自定义Realm的交互。

以下是ShiroConfig类的完整代码:

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * 创建ShiroFilterFactoryBean,用于定义Shiro的过滤器链。
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);

        // 定义拦截规则
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon"); // 登录页面无需认证
        filterChainDefinitionMap.put("/register", "anon"); // 注册页面无需认证
        filterChainDefinitionMap.put("/logout", "logout"); // 退出登录
        filterChainDefinitionMap.put("/**", "authc"); // 其他页面需要认证

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        bean.setLoginUrl("/login"); // 设置登录页面
        bean.setSuccessUrl("/home"); // 设置登录成功后的跳转页面
        bean.setUnauthorizedUrl("/403"); // 设置未授权页面

        return bean;
    }

    /**
     * 创建DefaultWebSecurityManager,用于管理Shiro的安全策略。
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm()); // 设置自定义Realm
        return securityManager;
    }

    /**
     * 创建自定义Realm,用于连接用户数据源。
     */
    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }
}

在上述配置中,ShiroFilterFactoryBean定义了Shiro的过滤器链,通过filterChainDefinitionMap配置了哪些路径需要认证,哪些路径可以匿名访问。DefaultWebSecurityManager则负责管理Shiro的安全策略,并通过setRealm方法与自定义的MyRealm类进行关联。这种分层的配置方式不仅清晰明了,还便于后续的扩展和维护。


3. 创建自定义Realm

Realm是Shiro的核心接口,用于连接用户数据源(如数据库)。在Shiro中,Realm的作用是提供用户信息、角色和权限数据。为了实现自定义的认证和授权逻辑,我们需要创建一个MyRealm类,继承自AuthorizingRealm

以下是MyRealm类的完整代码:

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class MyRealm extends AuthorizingRealm {

    /**
     * 授权逻辑,用于获取用户的角色和权限。
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = principals.getPrimaryPrincipal().toString();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 模拟从数据库中获取用户角色和权限
        if ("admin".equals(username)) {
            info.addRole("admin");
            info.addStringPermission("user:create");
            info.addStringPermission("user:delete");
        } else if ("user".equals(username)) {
            info.addRole("user");
            info.addStringPermission("user:view");
        }

        return info;
    }

    /**
     * 认证逻辑,用于验证用户身份。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = token.getPrincipal().toString();
        String password = new String((char[]) token.getCredentials()); // 获取用户输入的密码

        // 模拟从数据库中获取用户信息
        if (!"admin".equals(username) && !"user".equals(username)) {
            throw new UnknownAccountException("用户名不存在");
        }

        if (!"123".equals(password)) {
            throw new IncorrectCredentialsException("密码错误");
        }

        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

MyRealm类中,doGetAuthorizationInfo方法用于实现授权逻辑,通过PrincipalCollection获取当前用户的用户名,并根据用户名模拟从数据库中获取用户的角色和权限。doGetAuthenticationInfo方法则用于实现认证逻辑,通过AuthenticationToken获取用户输入的用户名和密码,并进行验证。这种分层的实现方式不仅符合面向对象的设计原则,还便于后续的扩展和维护。


4. 创建Controller

为了测试Shiro的认证和授权功能,我们需要创建一个简单的ShiroController类。通过这个Controller,我们可以模拟用户登录、访问受保护的资源以及退出登录的场景。

以下是ShiroController类的完整代码:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;

@RestController
public class ShiroController {

    /**
     * 模拟用户登录接口。
     */
    @GetMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);
            return "登录成功!欢迎 " + username;
        } catch (Exception e) {
            return "登录失败:" + e.getMessage();
        }
    }

    /**
     * 模拟用户访问主页接口。
     */
    @GetMapping("/home")
    public String home() {
        return "欢迎来到主页!";
    }

    /**
     * 模拟用户退出登录接口。
     */
    @GetMapping("/logout")
    public String logout() {
        SecurityUtils.getSubject().logout();
        return "退出成功!";
    }

    /**
     * 模拟用户访问未授权资源时的接口。
     */
    @GetMapping("/403")
    public String unauthorized() {
        return "您没有权限访问该页面!";
    }
}

ShiroController类中,/login接口用于模拟用户登录操作,通过SecurityUtils.getSubject()获取当前用户主体,并调用login方法进行登录。/home接口用于模拟用户访问受保护的资源,/logout接口用于模拟用户退出登录,/403接口则用于模拟用户访问未授权资源时的场景。这种设计不仅清晰明了,还便于后续的扩展和维护。


5. 启动项目并测试

完成上述配置后,启动Spring Boot项目。通过访问/login接口,可以模拟用户登录操作。登录成功后,用户可以访问/home页面,而未登录的用户将被重定向到登录页面。此外,通过访问/logout接口,用户可以退出登录。

注意事项

  1. 版本兼容性:在选择Shiro版本时,务必确保其与Spring Boot版本兼容,以避免潜在的兼容性问题。建议在项目启动前进行版本兼容性测试,确保系统的稳定性和可靠性。
  2. 数据库集成:在实际项目中,用户信息和权限数据通常存储在数据库中。可以通过Spring Data JPA或MyBatis等框架实现与数据库的交互,并在自定义Realm中加载用户数据。这种设计不仅提高了系统的灵活性,还便于后续的扩展和维护。
  3. 自定义过滤器:根据项目需求,可以自定义Shiro的过滤器,例如实现验证码登录、记住我功能或自定义登录逻辑。通过自定义过滤器,可以进一步提升系统的安全性和用户体验。
  4. 安全性:在生产环境中,密码应通过加密方式存储和验证,避免明文存储密码。可以使用Shiro提供的HashedCredentialsMatcher来实现密码的加密验证。此外,建议定期对系统进行安全审计,确保系统的安全性。
  5. 日志记录:建议在认证和授权逻辑中添加日志记录,以便在出现问题时能够快速定位和排查。通过日志记录,可以更好地监控系统的运行状态,及时发现潜在的安全问题。
posted @   软件职业规划  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示