Shiro源碼分析-初始化-Realm

在上一篇介紹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的比較。

相關文章
相關標籤/搜索