Spring Boot 如何整合 Shiro 实现轻量级权限管理系统?

来源:Apache教程作者:广州SEO公司头衔:草根站长
导读:本期聚焦于广州SEO公司创作的《Spring Boot 如何整合 Shiro 实现轻量级权限管理系统?》,敬请观看详情。权限控制几乎是所有后台系统的刚需,相比 Spring Security 的复杂配置,Shiro 以轻量、易上手的特点成为不少团队的首选。本文将完整讲解如何在 Spring Boot 项目中整合 Shiro,内容包括核心概念解析、依赖引入、自定义 Realm 实现认证与授权、ShiroConfig 配置类的编写、密码加密方案、登录接口实战以及常见坑点排查。通过一个可运行的完整示例,帮助你快速搭建一套基于角色的访问控制体系,适合中小型项目的权限管理场景。

Shiro 是 Apache 基金会旗下的开源安全框架,它把认证、授权、加密和会话管理等功能封装得简洁易用。相比 Spring Security 庞大的体系,Shiro 的学习曲线更平缓,配置更灵活,特别适合中小型项目快速搭建权限体系。本文将从零开始,演示如何在 Spring Boot 项目中整合 Shiro,实现一套完整的基于角色的权限管理系统。

Spring Boot 如何整合 Shiro 实现轻量级权限管理系统?

一、Shiro 核心概念速览

在动手写代码之前,先理清 Shiro 的几个核心组件,这对理解后面的配置至关重要。Shiro 的架构围绕三个核心概念展开:Subject、SecurityManager 和 Realm。

Subject 代表当前与系统交互的用户主体,可以是人、第三方服务或定时任务。它本质上是一个门面,所有安全操作都通过 Subject 发起。SecurityManager 是真正的安全管理者,管理所有 Subject 的安全操作,是 Shiro 的核心大脑,任何集成了 Shiro 的应用都必须配置它。Realm 则是数据桥梁,负责从数据库或其他数据源中读取用户的身份信息、角色和权限,Shiro 的认证与授权最终都依赖 Realm 提供的数据。

一次典型的认证流程是这样的:用户提交用户名和密码后,Subject 会把token 交给 SecurityManager,SecurityManager 再委托配置好的 Realm 去数据库中查询用户信息并完成比对。理解了这条链路,后面的代码就非常好懂了。

二、项目搭建与依赖引入

创建一个 Spring Boot 项目,引入 Shiro 相关依赖。推荐使用 shiro-spring-boot-web-starter,它与 Spring Boot 的自动装配机制结合得更好。

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.13.0</version>
</dependency>
<!-- 数据库访问,这里用 MyBatis-Plus 做示例 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

数据库设计上至少需要三张表:用户表(sys_user)、角色表(sys_role)以及用户角色关联表(sys_user_role)。如果需要更细粒度的控制,可以再加一张权限表和角色权限关联表。本文示例采用用户和角色两级结构,通过角色来控制接口访问权限。

三、自定义 Realm 实现认证与授权

Realm 是整个整合过程中最核心的类,需要继承 AuthorizingRealm 并重写两个方法:doGetAuthenticationInfo 负责认证,doGetAuthorizationInfo 负责授权。

@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 授权:查询当前用户的角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 查询用户角色,实际项目中从数据库读取
        Set<String> roles = sysUserService.findRolesByUsername(username);
        info.setRoles(roles);
        return info;
    }

    /**
     * 认证:校验用户名和密码
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SysUser user = sysUserService.findByUsername(username);
        if (user == null) {
            // 用户不存在,Shiro 会抛出 UnknownAccountException
            throw new UnknownAccountException("用户不存在");
        }
        // 第一个参数是 principals,第二个参数是数据库中的密码,第三个参数是 realm 名称
        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
    }
}

认证方法中返回的 SimpleAuthenticationInfo 携带了数据库中存储的加密密码,Shiro 会自动用配置的 CredentialsMatcher 将用户提交的明文密码加密后与它比对,比对失败则抛出 IncorrectCredentialsException。授权方法只在访问需要权限的接口时才被调用,Shiro 默认带有缓存机制,不会每次请求都查库。

四、编写 ShiroConfig 配置类

配置类中需要做三件事:注册 SecurityManager、配置密码匹配器、定义过滤规则。过滤规则决定了哪些路径需要登录、哪些路径需要特定角色才能访问。

@Configuration
public class ShiroConfig {

    @Autowired
    private UserRealm userRealm;

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 配置密码匹配器,采用 MD5 加盐并迭代两次
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("MD5");
        matcher.setHashIterations(2);
        matcher.setStoredCredentialsHexEncoded(true);
        userRealm.setCredentialsMatcher(matcher);
        manager.setRealm(userRealm);
        return manager;
    }

    /**
     * 配置路径过滤规则,注意使用 ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        bean.setLoginUrl("/login");
        bean.setUnauthorizedUrl("/403");

        Map<String, String> filterMap = new LinkedHashMap<>();
        // 登录接口和静态资源放行
        filterMap.put("/login", "anon");
        filterMap.put("/css/**", "anon");
        filterMap.put("/js/**", "anon");
        // admin 角色才能访问的接口
        filterMap.put("/admin/**", "authc,roles[admin]");
        // 其余接口都需要登录
        filterMap.put("/**", "authc");
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }
}

这里有几个关键点需要注意。过滤链使用 LinkedHashMap 保证顺序匹配,一旦匹配成功就不会继续往下匹配,所以放行规则必须写在拦截规则前面。anon 表示匿名访问,authc 表示必须认证通过,roles[admin] 表示需要具备 admin 角色,多个角色之间用逗号分隔表示或的关系。

密码加密方面,数据库中存储的密码应该在用户注册时用相同算法生成。例如密码明文是 123456,盐值是 username,迭代两次后生成的 MD5 值可以通过 SimpleHash 计算出来,注册逻辑和登录校验的算法参数必须完全一致,否则认证永远失败。

五、登录接口实战与注解授权

完成配置后,编写登录接口。登录的本质是把用户名密码包装成 UsernamePasswordToken 交给 Subject 处理。

@RestController
public class LoginController {

    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO dto) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token =
                new UsernamePasswordToken(dto.getUsername(), dto.getPassword());
        try {
            subject.login(token);
            return Result.success("登录成功");
        } catch (UnknownAccountException e) {
            return Result.error("用户不存在");
        } catch (IncorrectCredentialsException e) {
            return Result.error("密码错误");
        }
    }

    @GetMapping("/logout")
    public Result logout() {
        SecurityUtils.getSubject().logout();
        return Result.success("退出成功");
    }
}

除了过滤器链,Shiro 还支持方法级别的注解授权,常见的有 @RequiresRoles@RequiresPermissions。要在 Controller 上生效,需要开启 AOP 支持,可以在配置类上加 @Bean 注册 AuthorizationAttributeSourceAdvisor,并配合 DefaultAdvisorAutoProxyCreator 使用。注解方式比过滤链更灵活,适合控制到具体的方法级别。

@RestController
@RequestMapping("/user")
public class UserController {

    @RequiresRoles("admin")
    @GetMapping("/list")
    public Result listUsers() {
        // 只有 admin 角色才能调用该方法
        return Result.success(userService.list());
    }

    @RequiresPermissions("user:delete")
    @DeleteMapping("/{id}")
    public Result deleteUser(@PathVariable Long id) {
        userService.removeById(id);
        return Result.success("删除成功");
    }
}

六、常见坑点与排查思路

整合过程中最容易踩的坑有几个。第一,前后端分离项目中,默认的 Session 会话保持方式会遇到跨域 Cookie 问题,建议改用 JWT 方案,自定义一个继承 BasicHttpAuthenticationFilter 的过滤器,从请求头中解析 token。第二,使用注解授权时如果 AOP 代理没有生效,注解会被静默忽略,接口不再校验角色,这是很大的安全隐患,务必确认 advisor 配置正确。

第三,密码比对失败时优先检查加密算法、盐值、迭代次数三要素是否与注册逻辑一致。第四,Shiro 的 UnavailableSecurityManagerException 通常是因为在非 Web 线程中调用了 SecurityUtils.getSubject(),异步任务中需要手动绑定 Subject 或传递 SessionId。

最后说一下技术选型的考量。Shiro 适合快速开发、权限模型相对简单的场景;如果项目需要 OAuth2、SAML 等企业级安全特性,或者深度依赖 Spring 生态的 SpEL 表达式权限控制,Spring Security 会是更合适的选择。两者没有绝对优劣,关键是匹配项目规模和团队技术栈。

Spring BootShiro权限管理修改时间:2026-09-02 10:26:44

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编写,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260902/48871.html,基于非商业使用的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。