在上一篇介绍SecurityManager的初始化过程当中,也有realm的粗略介绍。
realm的概念在安全领域随处可见:
各类中间件的realm、spring security的realm、shiro的realm。。。以下:
tomcat的realm: http://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html
weblogic的realm:http://edocs.weblogicfans.net/wls/docs92/secintro/realm_chap.html
spring security的realm http://www.oschina.net/translate/spring-security-basic-authentication?lang=eng
tomcat官网对realm的定义是这样的: html
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类图以下: mysql
由图可见,Realm主要仍是认证、受权服务,并提供cache支持。
shiro提供了几种realm可供项目选择,通常项目中比较经常使用的应该是JdbcRealm,运行测试用例通常使用IniRealm。
开涛在讲解身份验证的章节中演示了两种realm的方式(ini、jdbc)
http://jinnianshilongnian.iteye.com/blog/2019547
1、Ini方式的realm: web
、spring
从图中可看出,shiro对于经过文本方式定义帐号、权限提供了Ini、Properties两种方式。SimpleAccountRealm类的users、roles集合分别用来保存帐号、权限信息。
还记得在前一篇介绍SecurityManager初始化中,关于realm的建立了么? sql
没错,就是这里。根据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 { ...... } }
niRealm的父类TextConfigurationRealm根据子类的users、roles配置完成解析操做 apache
//解析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); } }
//解析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); } } }
2、Jdbc方式的realm: tomcat
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。关于更详细的讲解放在后面的认证、受权部分。
3、RealmFactory:
Shiro不只支持Realm类型,还支持RealmFactory类型,在初始化的时候,若是配置中存在RealmFactory实现类,则直接调用其Collection<Realm> getRealms()方法。
该方式在多个realm的状况下很实用。
4、与Spring Security的比较:
Spring Security的Realm仅仅是用在basic认证方式。
Shiro的realm与Spring Security的UserDetailsService很是类似。可是命名确很是迷糊,下面进一步分析:
先看UserDetailsService接口定义: app
//根据用户名称获取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的比较。