Yii之登入、登出、驗證、授權理解
驗證Authentication的目的是驗證某個使用者是否就是他聲稱的那個使用者,常見的就是 使用者名稱+密碼 來確認使用者身份,通過更復雜的程式設計,可以實現其它的身份確認方法
授權Authorization就是對於身份明確的(即已經過“驗證”這一步的)使用者檢查他是否允許訪問特定的資源(他對一定的資源能幹什麼,能讀嗎,能寫嗎),即檢查是否符合某種許可權角色
Yii包含內建的auth框架,並且可以定製以滿足特定的需求。auth框架的核心點在於預定義的user應用程式元件(一個實現了IWebUser介面的物件,常常是id為user的CWebUser物件),該元件代表當前使用者的持久的身份資訊,在應用的任何地方,都可以用Yii::app()->user來訪問。IWebUser介面包含3個屬性方法getId()、getIsGuest()、getName()和非屬性方法checkAccess()。CWebUser常用的屬性有allowAutoLogin、id、name、isGuest、loginUrl、returnUrl,常用的方法有checkAccess()、getFlash()、hasFlash()、login()、logout()、setFlash()。
CWebUser需要和identity(常見的繼承鏈為UserIdentity--CUserIdentity--CBaseUserIdentity-->IUserIdentity)一起使用來實現實際的驗證演算法。IUserIdentity定義的方法有authenticate()、getId()、getIsAuthenticated、getName()、getPersistentStates()。常見的做法是從CUserIdentity派生自己的身份識別類(例如用OpenID驗證、LDAP驗證、Twitter OAuth驗證、Facebook connect驗證)。預設生成的app中,LoginForm這個Form Model的authenticate方法和login方法中構造了UserIdentity物件,並呼叫了其authenticate方法。LoginForm中還將UserIdentity物件作為引數傳遞給user元件的login方法(此時使用者身份資訊是已經認證過的),原型public booleanlogin
權威指南給出的身份驗證類是典型的做法:
上述程式碼中,authenticate()方法中,先用模型方法根據使用者名稱查詢使用者是否存在,然後進行密碼的Hash值比對(可以把具體演算法放入User模型的業務邏輯中) ,使用者名稱和密碼Hash值比對成功後,設定私有變數id(最終通過屬性方法getId()暴露該值),使用繼承自CUserIdentity的屬性errorCode,方法setState()。class UserIdentity extends CUserIdentity { private $id; public function authenticate() { $record = User::model()->findByAttributes(array('username' => $this->username)); if ($record === null) $this->errorCode = self::ERROR USERNAME INVALID; elseif ($record->password !== crypt($this->password, $record->password)) $this->errorCode = self::ERROR PASSWORD INVALID; else { $this->id = $record->id; $this->setState('title', $record->title); $this->errorCode = self::ERROR NONE; } return !$this->errorCode; } public function getId() { return $this->id; } }
這個身份驗證類的例項最終是傳遞給user元件的login方法的,任何通過呼叫CBaseUserIdentity::setState()儲存的狀態資訊都將傳遞給CWebUser,而CWebUser將這些資訊存放到持久儲存內(如session),之後這些資訊就可以類似CWebUser的屬性一樣被訪問。例如,上面的例子中呼叫了$this->setState('title', $record->title),登入成功後,就可以用Yii::app()->user->title來獲取該資訊。
有了身份驗證類,登入和登出是比較簡單的。登入主要包括:構造身份驗證類UserIdentity的例項,呼叫其方法authenticate()進行具體驗證,驗證成功後將例項作為引數傳遞給user元件的login()方法實現登入(這裡包含了必要的資訊的傳遞,session的記錄)。登出就是呼叫user元件的logout()方法(這裡包含了session的清理)。我們可以用user元件的isGuest屬性來判斷使用者是否是成功登入的(使用session時,就是讀取session中的有關設定)。
驗證的目的是為了授權,Yii內建了一種稱為訪問控制過濾器的授權模式,即檢查當前使用者是否能執行請求的控制器動作。訪問控制過濾器屬於過濾器的一種,所以在控制器中覆蓋filters()方法,並配置'accessControl'來實現,具體的過濾規則卻通過覆蓋accessRules()方法實現。對於簡單情形,使用訪問控制過濾器並設定規則就可以了,對於更復雜的授權體系,可以使用RBAC。
Yii通過authManager應用程式元件來實現RBAC授權體系。
RBAC的其中一個基本概念是“授權條目authorization”,即對做某事的許可。“授權條目”分為三類:操作operations,任務tasks,角色roles 。“授權條目”通過名字(字串)來唯一標識。授權條目可以有關聯的業務規則business rule(PHP程式碼片段,對授權條目進行訪問檢查時得以執行,只有該程式碼片段返回true,使用者才有對該條目的訪問許可)。
使用授權條目,就可以構建授權體系,RBAC授權體系是一個偏序圖,而不是樹,每個授權條目就是一個節點。有了授權體系,就可以把體系中的角色(這個角色包括授權條目中的3類條目)賦予應用程式的使用者,可以理解為使用者繫結到授權條目(節點)。使用者一旦賦予某個角色(節點),他就擁有那種角色(節點)具備的訪問許可。
在控制器的動作中,我們通過 if(Yii::app()->user->checkAccess('operation名字')){}這樣的程式碼來判斷當前使用者能否進行指定的操作,而RBAC體系通過在偏序圖中搜索來判斷當前使用者繫結的條目(節點)最終是否可以傳遞到指定的操作operation(節點)。值得注意的是,操作operation在RBAC授權體系中具有原子性(即已經是最小單位),operation名字和控制器動作action名字沒有必然的聯絡,理論上可以隨意起名字,因為最終的程式碼裡是通過Yii::app()->user->checkAccess來判斷的(從這個意義來說,RBAC體系就是個判斷體系),但是正如cookbook中指出的那樣,按一定的命名規則給節點(授權條目)起名字,能夠使RBAC體系更容易理解,也更容易管理。
在進行具體的工作中,第一步是配置authManager應用程式元件。常見的做法是配置成CDbAuthManager類,並指定資料庫連線名。
之後的定義授權體系是最複雜,需要仔細考慮的部分。這部分包括了定義授權條目,建立這些條目間的關係,將角色賦給應用程式的使用者。這些工作都是通過authManagerDe API來實現的。
定義條目用的API有:
CAuthManager::createRole
CAuthManager::createTask
CAuthManager::createOperation
建立關係(即構建偏序圖)的API有:
CAuthManager::addItemChild
CAuthManager::removeItemChild
CAuthItem::addChild
CAuthItem::removeChild
角色條目賦給使用者的API有:
CAuthManager::assign
CAuthManager::revoke
業務規則business rule的使用:無論是哪一種節點型別(role、task、operation)都可以關聯一個稱為業務規則的東西,甚至可以在使用者和節點繫結時(給節點)關聯業務規則。業務規則是一小段PHP程式碼,它在我們進行訪問檢查(通常是Yii::app()->user->checkAccess)時得以執行。可以這樣理解,不帶業務規則的節點,對它的許可權判斷是“已編譯”的,靜態的,而帶業務規則的節點,對它的許可權判斷是“執行時”的,動態的引數化的(Yii::app()->user->checkAccess('節點名', $params),$params是一個關聯陣列,其鍵必須和業務規則中預設的對應)。
使用預設角色:預設角色是隱式賦給每個使用者的角色,用來避免對大部分使用者常常遇到的許可權判斷情形進行顯式的賦權(當CWebUser::checkAccess被呼叫時,首先會檢查使用者是否賦予預設角色)。預設角色必須在CAuthManager::defaultRoles屬性中進行申明(在配置authManager時進行),並且因為預設角色會賦予給每個使用者,所以它常常需要相關的業務規則來決定指定的預設角色是否真正應用到該使用者,例如authenticated和admin:
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->name === "admin";';
$auth->createRole('admin', 'admin user', $bizRule);