You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:如何在Spring Boot+MySQL的Petclinic项目中集成Apache Shiro

Spring Boot PetClinic + Apache Shiro 集成入门指南

Hey there! I’ve worked through integrating Shiro with Spring Boot projects before, so let me walk you through a practical, no-nonsense starting plan tailored for the PetClinic app. Let’s break this down step by step:

1. 先添加Shiro依赖

首先给项目引入Shiro的Spring Boot启动器(如果用PetClinic默认的Thymeleaf视图层,记得加上Shiro-Thymeleaf集成依赖)。这里是Maven的配置示例:

<!-- Shiro Spring Boot Web 启动器 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.12.0</version> <!-- 使用最新稳定版本 -->
</dependency>

<!-- Shiro Thymeleaf 集成,用于页面级权限控制 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-thymeleaf</artifactId>
    <version>1.12.0</version>
</dependency>

2. 配置Shiro核心组件

创建一个ShiroConfig.java配置类,用来设置Shiro的核心模块:Realm(处理认证和授权逻辑)、SecurityManager(Shiro的核心控制组件)和拦截规则。

下面是适配PetClinic的入门配置示例:

@Configuration
public class ShiroConfig {

    // 自定义Realm:这里是对接PetClinic用户数据的核心
    @Bean
    public Realm petClinicRealm() {
        AuthorizingRealm realm = new AuthorizingRealm() {
            // 授权逻辑:获取当前登录用户的角色/权限
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                String userIdentifier = (String) principals.getPrimaryPrincipal();
                // 替换为实际数据库查询:比如从PetClinic的仓库中获取用户角色
                SimpleAuthorizationInfo authInfo = new SimpleAuthorizationInfo();
                // 示例:根据用户类型分配角色(OWNER、VET、ADMIN)
                if (userIdentifier.contains("vet@")) {
                    authInfo.addRole("VET");
                } else {
                    authInfo.addRole("OWNER");
                }
                return authInfo;
            }

            // 认证逻辑:验证用户名密码是否匹配PetClinic的用户数据
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                UsernamePasswordToken loginToken = (UsernamePasswordToken) token;
                String username = loginToken.getUsername();

                // 替换为实际仓库调用:比如ownerRepository.findByEmail(username)
                // 测试阶段可以用PetClinic的示例用户数据
                if ("george.francis@example.com".equals(username)) {
                    // 注意:数据库中要存储BCrypt加密后的密码,不要存明文!
                    return new SimpleAuthenticationInfo(username, "$2a$10$Z8OvQxQ8y8a9X9Y0Z1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P", getName());
                }
                throw new UnknownAccountException("PetClinic系统中未找到该用户");
            }
        };

        // 配置BCrypt密码匹配器,确保密码验证安全
        realm.setCredentialsMatcher(new HashedCredentialsMatcher("BCrypt"));
        return realm;
    }

    // 安全管理器:Shiro的核心控制组件
    @Bean
    public DefaultWebSecurityManager securityManager(Realm petClinicRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(petClinicRealm);
        return securityManager;
    }

    // Shiro过滤器:定义哪些路径需要认证/角色权限
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        filterFactory.setSecurityManager(securityManager);
        filterFactory.setLoginUrl("/login"); // 未认证用户跳转的登录页
        filterFactory.setSuccessUrl("/"); // 登录成功后跳转的首页
        filterFactory.setUnauthorizedUrl("/unauthorized"); // 权限不足时的跳转页

        // 配置拦截规则(顺序很重要!)
        Map<String, String> filterRules = new LinkedHashMap<>();
        // 静态资源和登录页允许匿名访问
        filterRules.put("/css/**", "anon");
        filterRules.put("/js/**", "anon");
        filterRules.put("/images/**", "anon");
        filterRules.put("/login", "anon");
        // 其他所有路径需要认证才能访问
        filterRules.put("/**", "authc");
        // 兽医专属页面仅允许VET角色访问
        filterRules.put("/vet/**", "roles[VET]");

        filterFactory.setFilterChainDefinitionMap(filterRules);
        return filterFactory;
    }

    // 启用Shiro Thymeleaf标签,支持页面级权限判断
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

3. 适配PetClinic的用户实体

PetClinic默认没有专门的用户实体,你可以选择:

  • 给现有实体(比如Owner或Vet)添加password(加密后存储)和role字段
  • 创建独立的User实体,关联到Owner/Vet等,专门存储认证信息

4. 实现登录/登出流程

创建一个简单的LoginController,用Shiro的SubjectAPI处理用户登录登出:

@Controller
public class LoginController {

    @GetMapping("/login")
    public String showLoginPage() {
        return "login"; // 指向自定义的登录页Thymeleaf模板
    }

    @PostMapping("/login")
    public String processLogin(@RequestParam String username, @RequestParam String password, Model model) {
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            currentUser.login(token);
            return "redirect:/";
        } catch (AuthenticationException e) {
            model.addAttribute("error", "用户名或密码错误");
            return "login";
        }
    }

    @GetMapping("/logout")
    public String processLogout() {
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }
}

5. 页面级权限控制

用Shiro的Thymeleaf标签根据用户角色或认证状态显示/隐藏内容,比如在PetClinic的头部导航中:

<!-- 仅向已认证用户显示登出链接 -->
<div shiro:authenticated>
    <a href="/logout">退出登录</a>
</div>

<!-- 仅向VET角色用户显示兽医控制台链接 -->
<div shiro:hasRole="VET">
    <a href="/vet/dashboard">兽医控制台</a>
</div>

避坑小提示

  • 永远存储加密后的密码(禁止明文),示例中用的BCrypt是推荐方案
  • 从小功能入手:先实现基础登录,再逐步添加角色权限控制
  • 用PetClinic的示例用户(比如george.francis@example.com)测试流程

内容的提问来源于stack exchange,提问作者Rodwan Bakkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:56:52