Shiro入门案例

wen java案例 3

Shiro入门案例详解:从零构建你的首个权限管理系统

Shiro入门案例

目录导读

  1. Shiro是什么?为什么需要它?
  2. 环境准备与核心依赖配置
  3. 第一个Shiro入门案例:身份认证(登录)实战
  4. 授权(权限控制)案例:基于角色的访问控制
  5. Shiro与Spring Boot整合的完整示例
  6. 常见问题与面试高频问答(FAQ)
  7. 总结与进阶学习路线

Shiro是什么?为什么需要它?

Apache Shiro是一个强大且易用的Java安全框架,提供认证(Authentication)授权(Authorization)会话管理(Session Management)加密(Cryptography) 四大核心功能,相比Spring Security,Shiro的API更加直观,配置更轻量,特别适合快速搭建中小型项目的权限模块。

核心关键词:Subject(当前用户)、SecurityManager(安全管理器)、Realm(数据源桥接)。

为什么不用Spring Security? 对于刚入门的新手,Shiro的学习曲线更平缓,其设计哲学是“让简单的事情变得简单”,且不强制与Spring容器耦合。


环境准备与核心依赖配置

基础环境:JDK 8+,Maven 3.6+,IDEA。

创建一个普通Maven项目,在pom.xml中引入Shiro核心依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.11.0</version>
</dependency>

核心组件说明

  • Subject:当前操作用户,是代码中直接交互的入口。
  • SecurityManager:核心调度器,负责协调Realm完成认证授权。
  • Realm:连接Shiro与数据源(数据库、配置文件)的桥梁。

第一个Shiro入门案例:身份认证(登录)实战

场景:使用shiro.ini配置文件模拟用户数据,实现登录验证。

步骤1:创建shiro.ini文件

[users]
admin = 123456
user = 123456

步骤2:编写认证代码

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class AuthenticationDemo {
    public static void main(String[] args) {
        // 1. 加载配置文件,创建SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        // 2. 获取当前Subject
        Subject subject = SecurityUtils.getSubject();
        // 3. 封装用户名密码
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        // 4. 执行登录
        try {
            subject.login(token);
            System.out.println("✅ 登录成功!用户:" + subject.getPrincipal());
        } catch (UnknownAccountException e) {
            System.out.println("❌ 用户名不存在!");
        } catch (IncorrectCredentialsException e) {
            System.out.println("❌ 密码错误!");
        } catch (LockedAccountException e) {
            System.out.println("❌ 账户被锁定!");
        }
        // 5. 退出登录
        subject.logout();
    }
}

运行结果:控制台输出“✅ 登录成功!用户:admin”。

关键点:Shiro的认证流程本质上是Subject.login()触发SecurityManager,最终由Realm进行数据比对。


授权(权限控制)案例:基于角色的访问控制

场景:验证当前用户是否拥有指定角色或权限。

扩展shiro.ini配置

[users]
admin = 123456, adminRole, managerRole
user = 123456, userRole
[roles]
adminRole = user:create, user:delete
userRole = user:view

授权代码演示

// 认证成功后,判断角色
if (subject.hasRole("adminRole")) {
    System.out.println("用户拥有管理员角色,可以执行删除操作");
}
// 判断权限(细粒度)
if (subject.isPermitted("user:delete")) {
    System.out.println("用户拥有删除权限");
} else {
    System.out.println("权限不足,拒绝访问");
}

执行效果:admin用户可执行删除,user用户提示权限不足。

理解:角色是权限的集合,Shiro支持hasRole()isPermitted()两种粒度检查,后者更灵活。


Shiro与Spring Boot整合的完整示例

在实际开发中,90%的案例都是与Spring Boot整合,以下为精简的配置方式:

依赖引入

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.11.0</version>
</dependency>

自定义Realm(核心)

@Component
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    // 认证逻辑
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        String username = (String) token.getPrincipal();
        User user = userService.findByUsername(username);
        if (user == null) {
            return null; // 返回null,Shiro抛出UnknownAccountException
        }
        return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
    }
    // 授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        Set<String> roles = userService.findRolesByUsername(username);
        Set<String> perms = userService.findPermissionsByUsername(username);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        info.setStringPermissions(perms);
        return info;
    }
}

ShiroConfig配置

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl("/login");
        factoryBean.setUnauthorizedUrl("/unauthorized");
        Map<String, String> filterChainMap = new LinkedHashMap<>();
        filterChainMap.put("/login", "anon");   // 匿名访问
        filterChainMap.put("/logout", "logout");
        filterChainMap.put("/admin/**", "authc, roles[admin]"); // 需要管理员角色
        filterChainMap.put("/**", "authc");     // 其他页面需要登录
        factoryBean.setFilterChainDefinitionMap(filterChainMap);
        return factoryBean;
    }
}

注意:URL拦截规则使用LinkedHashMap保证顺序,anon表示匿名可访问,authc表示需要登录,roles[]表示角色限制。


常见问题与面试高频问答(FAQ)

Q1:Shiro的认证流程中,密码是明文传输的吗? A:不是,生产环境必须加密,Shiro提供Md5HashSimpleHash工具类,常用做法是加盐MD5或BCrypt,并在CustomRealmdoGetAuthenticationInfo中返回带盐的AuthenticationInfo

Q2:ShiroFilterFactoryBeanauthcuser过滤器有什么区别? Aauthc强制要求用户必须登录,否则跳转登录页;user则允许“记住我”用户访问,如果你启用了rememberMe,访问受保护页面时,authc会拦截,而user不会。

Q3:如何在Shiro中实现“记住我”功能? A:在认证成功后,调用subject.getSession().setTimeout()设置过期时间,或者使用UsernamePasswordTokensetRememberMe(true)方法,同时在配置中设置cookiemaxAge

Q4:一个用户被多个角色限制怎么办? A:使用roles["admin,user"]表示需同时拥有两个角色;使用roles["admin,user"]filterChain中分隔符,或通过权限符号user:view, user:edit实现“或”逻辑。


总结与进阶学习路线

本案例已经带你完成了:

  • ✅ 理解了Shiro的四大核心架构
  • ✅ 通过shiro.ini完成了基础认证和授权
  • ✅ 掌握了Spring Boot整合的完整配置
  • ✅ 梳理了常见面试题与坑点

进阶建议

  • 学习缓存机制(整合Redis缓存角色权限)
  • 学习并发会话控制(单用户多点登录限制)
  • 学习分布式Session共享
  • 阅读官方文档《Shiro Reference》以及核心源码(DefaultSecurityManagerModularRealmAuthenticator

想快速掌握? 在实际项目中模仿本案例搭一个后台管理模块,实战体验权限校验带来的安全感。

记住:权限系统的本质是“信任最小化”——只给你需要的权限,不多给一个,Shiro正是帮助你优雅实现这一哲学的最佳编程工具之一。

上一篇ACL案例

下一篇CAS客户端案例

抱歉,评论功能暂时关闭!