什么是Apache Shiro?
Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一系列直观的身份验证、授权、加密和会话管理API。在Java后端开发面试中,Shiro作为一个重要的安全框架,经常出现在Java安全框架相关的面试题中。与Spring Security相比,Shiro更加轻量级,配置更简单,因此在许多中小型项目中得到了广泛应用。
身份验证 (Authentication)
验证用户身份,支持多种认证方式,包括用户名密码、OAuth、LDAP等。
授权 (Authorization)
控制用户访问权限,支持基于角色和权限的访问控制,灵活配置资源访问规则。
会话管理 (Session Management)
提供跨Web、EJB、RMI等环境的会话管理功能,支持分布式会话。
加密 (Cryptography)
提供密码加密、哈希等安全功能,确保数据在存储和传输过程中的安全性。
Shiro十大面试题深度解析
以下是针对Java Shiro面试题整理的十大核心问题,涵盖了从基础概念到高级应用的各个方面。每个问题都配有详细的解答和示例代码,帮助您深入理解Shiro框架的核心机制。
1. 请解释Shiro中的认证流程?
Shiro的认证流程主要涉及以下几个步骤:
- 创建 Subject 对象,代表当前用户。
- 收集用户凭证(通常是用户名和密码)。
- 创建 UsernamePasswordToken 对象。
- 调用 subject.login(token) 进行认证。
- Shiro内部调用 Realm 进行身份验证。
- 返回认证结果。
2. 如何实现自定义Realm?
自定义Realm需要继承 AuthorizingRealm 类,并重写两个核心方法:
public class CustomRealm extends AuthorizingRealm {
// 认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
// 从数据库获取用户信息
User user = userService.findByUsername(username);
if (user == null) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
// 授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
User user = userService.findByUsername(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(user.getRoles()); // 添加角色
info.addStringPermissions(user.getPermissions()); // 添加权限
return info;
}
}
3. Shiro中的权限表达式有哪些?
Shiro支持多种权限表达式,常用的是Shiro标签和注解:
- <shiro:hasRole name="admin">:检查用户是否具有指定角色。
- <shiro:hasPermission name="user:create">:检查用户是否具有指定权限。
- @RequiresRoles("admin"):方法级角色检查。
- @RequiresPermissions("user:create"):方法级权限检查。
4. Shiro的会话管理是如何工作的?
Shiro提供了独立的会话管理模块,不依赖于Servlet容器。会话管理涉及以下核心组件:
- SessionManager:会话管理器,负责创建和维护会话。
- SessionDAO:会话数据访问对象,负责会话的持久化。
- SessionListener:会话监听器,监听会话的生命周期事件。
5. 如何实现分布式会话?
在分布式环境中,可以使用Redis或Memcached作为会话存储后端:
@Bean
public RedisSessionDAO redisSessionDAO(RedisTemplate redisTemplate) {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisTemplate(redisTemplate);
sessionDAO.setKeyPrefix("shiro:session:");
return sessionDAO;
}
@Bean
public DefaultWebSessionManager sessionManager(RedisSessionDAO sessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(sessionDAO);
sessionManager.setGlobalSessionTimeout(1800000); // 30分钟
return sessionManager;
}
6. Shiro会话与Spring Session如何集成?
可以通过配置 SessionManager 来集成Spring Session:
- 引入Spring Session依赖。
- 配置 SpringSessionBasedSessionManager。
- 将Shiro的会话管理器设置为Spring Session管理器。
7. Shiro如何集成Ehcache缓存?
Shiro支持多种缓存实现,Ehcache是常用的选择之一:
@Bean
public EhCacheManager ehCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
@Bean
public SecurityManager securityManager(EhCacheManager ehCacheManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
securityManager.setCacheManager(ehCacheManager);
return securityManager;
}
8. Shiro与Spring Boot如何集成?
Spring Boot集成Shiro主要步骤:
- 引入 spring-boot-starter-shiro 依赖。
- 配置 ShiroFilterFactoryBean。
- 定义 Realm 实现。
- 配置 SecurityManager。
- 启用Shiro注解支持。
9. Shiro中的过滤器链如何配置?
过滤器链配置决定了URL的访问控制规则:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
Map filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon"); // 匿名访问
filterChainDefinitionMap.put("/logout", "logout"); // 登出
filterChainDefinitionMap.put("/admin/", "roles[admin]"); // 角色限制
filterChainDefinitionMap.put("/", "authc"); // 需要认证
factory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return factory;
}
10. Shiro中的密码加密如何实现?
Shiro提供了多种密码加密算法支持:
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256"); // 使用SHA-256算法
matcher.setHashIterations(1024); // 迭代次数
return matcher;
}
@Bean
public CustomRealm customRealm(HashedCredentialsMatcher matcher) {
CustomRealm realm = new CustomRealm();
realm.setCredentialsMatcher(matcher);
return realm;
}
Shiro常见集成方案
在实际项目中,Shiro通常需要与其他技术栈集成。以下是几种常见的集成方案及其配置要点。
| 集成技术 | 配置要点 | 适用场景 |
|---|---|---|
| Spring Boot | 使用 ShiroAutoConfiguration 自动配置 | 微服务架构、快速开发 |
| MyBatis | 在 Realm 中注入Mapper进行数据库操作 | 数据访问层集成 |
| Redis | 使用 RedisSessionDAO 实现分布式会话 | 分布式系统、集群部署 |
| Ehcache | 配置 EhCacheManager 作为缓存管理器 | 权限缓存、会话缓存 |
| Vue/React | 前后端分离,使用JWT替代Session | SPA应用、移动端 |
Shiro核心源码解析
理解Shiro的源码结构有助于深入掌握其工作原理。以下是核心模块的分析:
安全管理器是Shiro的核心,负责协调所有安全操作。它委托给具体的Realm进行身份验证和授权。
Subject代表当前用户的安全视图,所有安全操作都通过Subject进行。它实际上是SecurityManager的代理。
Realm是Shiro与应用安全数据之间的桥梁,负责从数据源(如数据库、LDAP)获取用户信息。
过滤器链解析器负责解析URL与过滤器链的映射关系,决定请求是否需要认证或授权。