Shiro源码分析-初始化-Realm

本文介绍了Shiro中的Realm概念,包括IniRealm和JdbcRealm的使用方法,并对比了SpringSecurity的UserDetailsService。Realm作为Shiro的核心组件,负责管理账号、密码、角色及权限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇介绍SecurityManager的初始化过程中,也有realm的粗略介绍。
realm的概念在安全领域随处可见:
各种中间件的realm、spring security的realm、shiro的realm。。。如下:
tomcat的realm:[url]http://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html[/url]
weblogic的realm:[url]http://edocs.weblogicfans.net/wls/docs92/secintro/realm_chap.html[/url]
spring security的realm[url]http://www.oschina.net/translate/spring-security-basic-authentication?lang=eng[/url]

tomcat官网对realm的定义是这样的:

A Realm is a "database" of usernames and passwords that identify valid users of a web application (or set of web applications), plus an enumeration of the list of roles associated with each valid user. You can think of roles as similar to groups in Unix-like operating systems, because access to specific web application resources is granted to all users possessing a particular role (rather than enumerating the list of associated usernames). A particular user can have any number of roles associated with their username.

我个人理解realm相当于管理账号密码、角色、权限的仓库。有异议的,欢迎一起讨论。

原谅我的啰嗦,先进入正题。
Shiro的realm类图如下:
[img]http://dl2.iteye.com/upload/attachment/0095/6865/a8f08c94-e50e-3f15-9da0-7a6fe369c35b.jpg[/img]
由图可见,Realm主要还是认证、授权服务,并提供cache支持。

shiro提供了几种realm可供项目选择,一般项目中比较常用的应该是JdbcRealm,运行测试用例一般使用IniRealm。
开涛在讲解身份验证的章节中演示了两种realm的方式(ini、jdbc)
[url]http://jinnianshilongnian.iteye.com/blog/2019547[/url]

一、Ini方式的realm:
[img]http://dl2.iteye.com/upload/attachment/0095/7516/d28c2930-2c5c-3bf8-94f9-b15fd743f043.jpg[/img]
从图中可看出,shiro对于通过文本方式定义账号、权限提供了Ini、Properties两种方式。SimpleAccountRealm类的users、roles集合分别用来保存账号、权限信息。

还记得在前一篇介绍SecurityManager初始化中,关于realm的创建了么?
[img]http://dl2.iteye.com/upload/attachment/0095/7518/534f5237-0de9-3939-9c8c-c36c58a5a855.jpg[/img]
没错,就是这里。根据ini配置中的users、roles段落来解析成IniRealm的对象实例。
下面,简单跟踪一下代码吧:

//IniRealm构造函数会调用此方法完成解析操作
private void processDefinitions(Ini ini) {
if (CollectionUtils.isEmpty(ini)) {
log.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName());
return;
}

Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME);
if (!CollectionUtils.isEmpty(rolesSection)) {
log.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME);
//解析roles段落交给父类完成
processRoleDefinitions(rolesSection);
}

Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME);
if (!CollectionUtils.isEmpty(usersSection)) {
log.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME);
//解析users段落交给父类完成
processUserDefinitions(usersSection);
} else {
......
}
}

IniRealm的父类TextConfigurationRealm根据子类的users、roles配置完成解析操作

//解析roles,并构造SimpleRole对象
protected void processRoleDefinitions(Map<String, String> roleDefs) {
if (roleDefs == null || roleDefs.isEmpty()) {
return;
}
for (String rolename : roleDefs.keySet()) {
String value = roleDefs.get(rolename);

SimpleRole role = getRole(rolename);
if (role == null) {
role = new SimpleRole(rolename);
add(role);
}

Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver());
role.setPermissions(permissions);
}
}


//解析users,并构造SimpleAccount对象
protected void processUserDefinitions(Map<String, String> userDefs) {
if (userDefs == null || userDefs.isEmpty()) {
return;
}
for (String username : userDefs.keySet()) {

String value = userDefs.get(username);

String[] passwordAndRolesArray = StringUtils.split(value);

String password = passwordAndRolesArray[0];

SimpleAccount account = getUser(username);
if (account == null) {
account = new SimpleAccount(username, password, getName());
add(account);
}
account.setCredentials(password);

if (passwordAndRolesArray.length > 1) {
for (int i = 1; i < passwordAndRolesArray.length; i++) {
String rolename = passwordAndRolesArray[i];
account.addRole(rolename);

SimpleRole role = getRole(rolename);
if (role != null) {
account.addObjectPermissions(role.getPermissions());
}
}
} else {
account.setRoles(null);
}
}
}


二、Jdbc方式的realm:

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=root
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm

由于shiro支持在ini中配置依赖注入,那么JdbcRealm中的sql信息也是可配置的。

jdbcRealm.authenticationQuery=用户查询
jdbcRealm.userRolesQuery=角色查询
jdbcRealm.permissionsQuery=权限查询

当然,如果不习惯这种方式,可以直接自定义Realm,并继承自AuthorizingRealm或者JdbcRealm都可以。
JdbcRealm通过查询数据库的认证实体、角色、权限,构造的对象分别是:SimpleAuthenticationInfo、SimpleAuthorizationInfo。
实际上,IniRealm方式构造的SimpleAccount、SimpleRole与JdbcRealm方式构造的SimpleAuthenticationInfo、SimpleAuthorizationInfo一一对应,且都实现认证接口AuthenticationInfo、授权接口AuthorizationInfo。关于更详细的讲解放在后面的认证、授权部分。

三、RealmFactory:
Shiro不仅支持Realm类型,还支持RealmFactory类型,在初始化的时候,如果配置中存在RealmFactory实现类,则直接调用其Collection<Realm> getRealms()方法。
该方式在多个realm的情况下很实用。

四、与Spring Security的比较:
Spring Security的Realm仅仅是用在basic认证方式。
Shiro的realm与Spring Security的UserDetailsService非常相似。但是命名确非常迷糊,下面进一步分析:
先看UserDetailsService接口定义:

//根据用户名称获取UserDetails
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException;
}

UserDetails接口定义:

public interface UserDetails extends Serializable {
//获取授权的集合
Collection<GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}

由此可看出,Spring Security的UserDetailsService获取的UserDetails已经拥有了授权信息。只是从接口命名中无法得知。

而Shiro的realm是把职责分解了,层次更清晰些。
后续在分析shiro的过程中,会增加与SpringSecurity的比较。
<think>好的,我需要帮助用户分析Apache Shiro 1.12.0的源码结构以及核心功能的实现细节。首先,我得回忆一下用户提供的引用内容,看看有没有相关的信息可以利用。 引用[1]提到Shiro是一个Java安全框架,提供身份验证、授权、密码学和会话管理。这说明核心模块可能包括这些部分。用户可能想知道这些模块在源码中是如何组织的。 引用[2]和[3]讨论了URL处理中的问题,比如去掉JSESSIONID和处理中文路径报错。这说明Shiro的Web模块和过滤器链可能是关键部分,需要特别注意。 接下来,我需要整理Shiro的源码结构。通常,大型项目会分模块,比如核心模块、Web支持、工具类等。根据引用内容,可以推测Shiro的源码可能分为core、web、crypto等模块。比如,身份验证可能在authc包下,授权在authz,会话管理在session。 核心功能实现方面,身份验证可能涉及Subject、SecurityManager、Realm这些类。SecurityManager作为核心,协调各个组件。Realm负责获取安全数据,用户可能需要自定义Realm。 授权部分,用户是否有权限访问资源,可能通过注解或过滤器链实现。权限检查可能使用AuthorizingSecurityManager,结合Realm中的授权信息。 会话管理方面,Shiro提供了自己的会话机制,可能存储在内存或Redis中。引用[3]提到了过滤器处理URL的问题,所以Web模块的过滤器链设计很重要,比如ShiroFilter和AbstractShiroFilter。 密码学模块可能包括散列、加密等功能,比如Md5Hash和AesCipherService类。用户可能需要了解如何配置加密算法。 源码解析步骤方面,建议用户从pom.xml开始,查看模块依赖。然后从配置类入手,比如IniSecurityManagerFactory,了解初始化流程。调试入口点可能从ShiroFilter开始,跟踪请求处理流程。 核心类包括SecurityManager、Subject、RealmSecurityManager接口的实现DefaultSecurityManager是关键,需要分析其如何协调认证、授权和会话。 可能的问题包括如何扩展Realm,如何自定义过滤器,以及会话存储的配置方法。例如,引用[3]提到通过注入过滤器子类解决中文路径问题,说明用户可能需要自定义过滤器。 最后,生成相关问题时,需要考虑用户可能关心的性能优化、扩展点、安全配置、异常处理等。结合引用中的内容,比如URL处理问题,可以提出如何自定义过滤器的问题。</think>### Apache Shiro 1.12.0 源码解析及核心功能实现 #### 一、源码结构分析 Apache Shiro 1.12.0 的源码采用模块化设计,主要模块包括: 1. **核心模块** (`shiro-core`) 包含安全框架的基础接口和实现,如`SecurityManager`、`Subject`、`Realm`等,代码路径为`org.apache.shiro.*`。 2. **Web支持模块** (`shiro-web`) 提供与Servlet容器集成的过滤器链和会话管理,如`ShiroFilter`和`SessionManager`,路径为`org.apache.shiro.web.*`[^1][^3]。 3. **密码学模块** (`shiro-crypto`) 实现散列算法(如`Md5Hash`)和加密工具(如`AesCipherService`)。 4. **工具模块** 包含配置解析(`Ini`)、缓存(`CacheManager`)等辅助功能。 #### 二、核心功能实现细节 1. **身份认证(Authentication)** - 入口类`Subject.login(AuthenticationToken)`触发认证流程。 - `SecurityManager`协调多个`Realm`进行凭证验证,核心代码在`AuthenticatingSecurityManager`中[^1]。 - 示例流程: $$ \text{Subject} \rightarrow \text{SecurityManager} \rightarrow \text{ModularRealmAuthenticator} \rightarrow \text{Realm.getAuthenticationInfo()} $$ 2. **授权(Authorization)** - 权限检查通过`Subject.isPermitted(String)`实现,底层由`AuthorizingSecurityManager`调用`Realm`的授权方法[^1]。 - 支持基于角色的访问控制(RBAC),核心逻辑在`AuthorizingRealm`中。 3. **会话管理(Session Management)** - 提供`SessionDAO`接口支持会话持久化(如`MemorySessionDAO`或`RedisSessionDAO`)。 - Web模块通过`WebSessionManager`与Servlet会话集成,可自定义URL中的会话ID处理[^2]。 4. **过滤器链(Filter Chain)** - `ShiroFilter`根据URL模式匹配过滤器链,如`anon`(匿名访问)、`authc`(需认证)。 - 自定义过滤器需继承`AccessControlFilter`并注册到`ShiroFilterFactoryBean`[^3]。 #### 三、关键源码解析步骤 1. **从配置入口切入** - 分析`IniSecurityManagerFactory`或`ShiroFilterFactoryBean`初始化过程。 2. **调试认证流程** - 断点跟踪`DefaultSecurityManager.login()`方法,观察`Realm`调用顺序。 3. **Web请求处理** - 研究`AbstractShiroFilter.doFilterInternal()`如何代理Servlet请求[^2]。 #### 四、核心类关系图 ``` Subject (当前用户) ↑ 委托 SecurityManager (安全管理中心) ↑ 聚合 Realm (数据源) ↑ 实现 Authorizer/Authenticator (认证授权器) ``` #### 五、扩展与调试建议 1. **自定义Realm** 继承`AuthorizingRealm`并重写`doGetAuthenticationInfo`和`doGetAuthorizationInfo`方法。 2. **解决URL编码问题** 通过重写`WebUtils.toHttpServletRequest()`方法修改URL解码逻辑[^3]。 3. **会话存储优化** 替换默认的`MemorySessionDAO`为`RedisSessionDAO`实现分布式会话。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值