Java Apache Shiro权限管理框架配置与自定义Realm讲座
一、引言
大家好,欢迎来到今天的讲座。今天我们来聊聊Java中非常流行的权限管理框架——Apache Shiro。如果你是第一次听说Shiro,不要担心,我会用轻松诙谐的语言,带你一步步了解这个强大的工具,并教你如何配置它以及自定义Realm。我们还会引用一些国外的技术文档,确保你不仅能理解概念,还能掌握实际操作。
二、什么是Apache Shiro?
首先,让我们了解一下什么是Apache Shiro。Shiro(日语中“城”的意思)是一个强大而灵活的开源安全框架,它可以帮助开发者轻松地实现身份验证、授权、加密和会话管理等功能。Shiro的核心设计理念是简单易用,同时提供足够的灵活性来满足复杂的应用需求。
Shiro的主要功能包括:
- 身份验证(Authentication):确认用户的身份,确保他们是合法用户。
- 授权(Authorization):控制用户可以访问哪些资源或执行哪些操作。
- 加密(Cryptography):提供加密和解密功能,保护敏感数据。
- 会话管理(Session Management):管理用户的会话信息,无论是在Web应用还是非Web应用中。
- 记住我(Remember Me):允许用户在关闭浏览器后仍然保持登录状态。
三、为什么选择Shiro?
在Java世界里,有很多权限管理框架可以选择,比如Spring Security、JSecurity(Shiro的前身)等。那么,为什么我们要选择Shiro呢?以下是几个理由:
- 简单易用:Shiro的API设计非常直观,学习曲线平缓,适合初学者快速上手。
- 灵活性高:Shiro提供了丰富的扩展点,允许开发者根据自己的需求进行定制。
- 轻量级:相比其他框架,Shiro的依赖较少,启动速度快,占用资源少。
- 社区活跃:Shiro有一个庞大的开发者社区,遇到问题时可以很容易找到解决方案。
- 广泛支持:Shiro不仅支持Web应用,还支持桌面应用、命令行应用等多种场景。
四、Shiro的基本概念
在深入讲解配置和自定义Realm之前,我们需要先了解一些Shiro的基本概念。这些概念是理解和使用Shiro的基础。
-
Subject:代表当前用户或客户端。每个Subject都可以进行身份验证和授权操作。Shiro通过
Subject
类来管理用户的状态。Subject currentUser = SecurityUtils.getSubject();
-
Principal:表示用户的身份标识,通常是用户名或用户ID。一个Subject可以有多个Principal。
-
Realm:Realm是Shiro与外部安全数据源(如数据库、LDAP、文件等)之间的桥梁。它负责从数据源中获取用户的身份信息和权限信息。你可以将Realm理解为Shiro的安全策略库。
-
AuthenticationToken:用于传递用户提供的凭证(如用户名和密码),以便进行身份验证。最常见的
AuthenticationToken
是UsernamePasswordToken
。 -
Permission:表示用户可以执行的操作或访问的资源。权限通常以字符串的形式表示,例如
user:create
表示用户可以创建新用户。 -
Role:角色是一组权限的集合。一个用户可以拥有多个角色,每个角色可以包含多个权限。
-
Session:Shiro的会话管理功能允许你在不同类型的环境中管理用户的会话信息。无论是Web应用还是非Web应用,Shiro都能提供一致的会话管理API。
五、Shiro的配置方式
Shiro的配置非常灵活,支持多种配置方式。我们可以使用XML、YAML、Java代码等方式来配置Shiro。为了让大家更好地理解,我们将重点介绍最常用的两种配置方式:基于shiro.ini
的配置和基于Java代码的配置。
1. 基于shiro.ini
的配置
shiro.ini
是Shiro默认的配置文件格式。它使用简单的键值对语法,易于阅读和维护。以下是一个典型的shiro.ini
配置示例:
[main]
# 配置Realm
myRealm = com.example.MyCustomRealm
securityManager.realm = $myRealm
# 配置Session管理器
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
[users]
# 定义用户及其密码
admin = admin123, admin
user = user123, user
[roles]
# 定义角色及其权限
admin = *
user = user:*
[urls]
# 定义URL的访问控制规则
/admin/** = authc, roles[admin]
/user/** = authc, roles[user]
/** = anon
在这个配置文件中,我们定义了一个自定义的Realm MyCustomRealm
,并设置了两个用户(admin
和user
)及其对应的密码和角色。我们还定义了URL的访问控制规则,确保只有具有相应角色的用户才能访问特定的URL。
2. 基于Java代码的配置
除了使用shiro.ini
,我们还可以通过Java代码来配置Shiro。这种方式更加灵活,适合需要动态配置的场景。以下是一个基于Java代码的Shiro配置示例:
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
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;
@Configuration
public class ShiroConfig {
@Bean
public Realm myRealm() {
// 返回自定义的Realm实例
return new MyCustomRealm();
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(securityManager);
// 定义过滤器链
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/admin/**", "authc, roles[admin]");
filterChainDefinitionMap.put("/user/**", "authc, roles[user]");
filterChainDefinitionMap.put("/**", "anon");
filter.setFilterChainDefinitionMap(filterChainDefinitionMap);
return filter;
}
}
在这个配置类中,我们通过Spring的@Configuration
注解来定义Shiro的Bean。我们创建了一个自定义的MyCustomRealm
,并将其设置为SecurityManager
的Realm。然后,我们使用ShiroFilterFactoryBean
来定义URL的访问控制规则。
六、自定义Realm
接下来,我们来探讨如何自定义Realm。自定义Realm是Shiro的核心功能之一,它允许你根据自己的业务需求,从不同的数据源中获取用户的身份信息和权限信息。下面我们将详细介绍如何创建一个自定义的Realm。
1. 实现Realm
接口
要创建一个自定义的Realm,你需要实现org.apache.shiro.realm.Realm
接口。这个接口定义了三个核心方法:
getAuthenticationInfo(AuthenticationToken token)
:用于验证用户的身份信息。getAuthorizationInfo(PrincipalCollection principals)
:用于获取用户的权限信息。supports(AuthenticationToken token)
:用于判断当前Realm是否支持给定的AuthenticationToken
。
以下是一个简单的自定义Realm示例:
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class MyCustomRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
// 模拟从数据库中查询用户信息
if ("admin".equals(username) && "admin123".equals(password)) {
return new SimpleAuthenticationInfo(username, password, getName());
} else {
throw new AuthenticationException("Invalid username or password");
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
// 模拟从数据库中查询用户的角色和权限
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
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
}
在这个自定义Realm中,我们重写了doGetAuthenticationInfo
和doGetAuthorizationInfo
方法。doGetAuthenticationInfo
方法用于验证用户的用户名和密码,而doGetAuthorizationInfo
方法用于获取用户的角色和权限。我们还实现了supports
方法,确保这个Realm只支持UsernamePasswordToken
类型的认证令牌。
2. 使用自定义Realm
一旦你创建了自定义Realm,接下来就是将其集成到Shiro的配置中。我们已经在前面的shiro.ini
和Java配置中展示了如何配置自定义Realm。这里再简单回顾一下:
-
在
shiro.ini
中,你可以通过以下方式配置自定义Realm:[main] myRealm = com.example.MyCustomRealm securityManager.realm = $myRealm
-
在Java代码中,你可以通过以下方式配置自定义Realm:
@Bean public Realm myRealm() { return new MyCustomRealm(); } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; }
七、Shiro的高级功能
除了基本的身份验证和授权功能,Shiro还提供了一些高级功能,帮助你构建更复杂的安全机制。下面我们来介绍其中的几个重要功能。
1. 记住我(Remember Me)
Shiro的“记住我”功能允许用户在关闭浏览器后仍然保持登录状态。要启用这个功能,你需要在shiro.ini
中添加以下配置:
[main]
rememberMeManager = org.apache.shiro.web.mgt.CookieRememberMeManager
securityManager.rememberMeManager = $rememberMeManager
rememberMeCookie = org.apache.shiro.web.servlet.SimpleCookie
rememberMeCookie.name = rememberMe
rememberMeCookie.httpOnly = true
rememberMeManager.cookie = $rememberMeCookie
# 启用记住我功能
authc = org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authc.rememberMeParameter = rememberMe
在Java代码中,你可以通过以下方式启用“记住我”功能:
@Bean
public RememberMeManager rememberMeManager() {
CookieRememberMeManager manager = new CookieRememberMeManager();
manager.setCipherKey(Base64.decode("kPH+bIxk5D2deZLVgyloOw=="));
return manager;
}
@Bean
public Cookie rememberMeCookie() {
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);
return cookie;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRememberMeManager(rememberMeManager());
return manager;
}
2. 多Realm支持
Shiro支持多Realm配置,这意味着你可以同时使用多个Realm来验证用户的身份信息。这对于需要从多个数据源(如数据库、LDAP、文件等)获取用户信息的场景非常有用。要启用多Realm支持,你只需要在shiro.ini
中定义多个Realm,并将它们添加到SecurityManager
中:
[main]
realm1 = com.example.Realm1
realm2 = com.example.Realm2
securityManager.realms = $realm1, $realm2
在Java代码中,你可以通过以下方式配置多Realm:
@Bean
public Collection<Realm> realms() {
List<Realm> realmList = new ArrayList<>();
realmList.add(new Realm1());
realmList.add(new Realm2());
return realmList;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealms(realms());
return manager;
}
3. 会话管理
Shiro提供了强大的会话管理功能,允许你在不同的环境中管理用户的会话信息。你可以通过SessionManager
来配置会话的超时时间、存储方式等。以下是一个简单的会话管理配置示例:
[main]
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
# 设置会话超时时间为30分钟
sessionManager.globalSessionTimeout = 1800000
在Java代码中,你可以通过以下方式配置会话管理:
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setGlobalSessionTimeout(1800000); // 30分钟
return manager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setSessionManager(sessionManager());
return manager;
}
八、总结
通过今天的讲座,我们深入了解了Apache Shiro权限管理框架的配置和自定义Realm。我们从Shiro的基本概念入手,逐步介绍了如何使用shiro.ini
和Java代码来配置Shiro,以及如何创建自定义的Realm来满足复杂的业务需求。最后,我们还探讨了一些Shiro的高级功能,如“记住我”、多Realm支持和会话管理。
希望今天的讲座能帮助你更好地理解和使用Apache Shiro。如果你有任何问题或建议,欢迎随时提问。谢谢大家的参与,期待下次再见!