一個站點上面最基本都會有三種使用者角色,未登入使用者、已登入使用者和管理者。這一次我們就來看看在revel架構下如何進行權限控制。
因為revel是MVC結構的,每一個url其實都會映射到一個具體的Controller.Action上面,是以權限控制落到實處就是對Action的通路進行控制。
那麼思路是如下:
1. 有一個方法能夠判定目前是什麼角色。
2. 有一地方定義了每一個Action的通路權限要求。
3. 有一個方法能夠在調用所有Action之前被調用,而且能夠判定是否還要繼續調用Action。
我們一項一項來解決。
存儲目前使用者的角色資訊
先定義一個角色類型如下。
type Role int
const (
AnonymousRole Role = iota
UserRole
AdminRole
)
定義個常量,用于在session裡面存放目前使用者的角色類型。
const (
CSessionRole = "CSessionRole"
然後在處理使用者登陸的方法中在session裡儲存角色資訊。
func (c *Account) HandleLogin(email, password string) revel.Result {
//在這裡處理登陸邏輯
c.Session[CSessionRole] = UserRole
//在這裡處理跳轉和頁面渲染
}
func (c *Account) HandleAdminLogin(account, password string) revel.Result {
c.Session[CSessionRole] = AdminRole
我們現在可以從session中讀取CSessionRole的值來判定目前使用者是什麼角色了。
定義Action的通路權限
在Controller層定義一個map,用于存放action的權限資訊,如下。
func initAuthMap() {
authMap = make(map[string]Role)
authMap["account.login"] = AnonymousRole
authMap["account.logout"] = UserRole
authMap["admin.index"] = AdminRole
要注意的是initAuthMap需要在Controller包的init函數中調用,以進行權限控制初始化。
檢查通路權限
revel架構提供的InterceptFunc(攔截方法)能夠将一個方法注入到所有Action的調用之前或之後,這就給權限控制留出了空間。
讓我們先定義一個方法用于檢查權限。
func checkAuthentication(c *revel.Controller) revel.Result {
//擷取目前登陸使用者的角色資訊
userRole, isExists := c.Session[util.CSessionRole]
if !isExists {
userRole = AnonymousRole
}
//擷取緊接着要調用的Action的名稱
action := strings.ToLower(c.Action)
//擷取相關action的權限定義
if requiredRole, isExists = authMap[action]; isExists {
//判斷權限,如果權限要求不相符
if requiredRole != userRole {
//跳轉到首頁
return c.Redirect((*Application).Index)
}
//傳回nil表示可以接着調用後面的Action,在這裡就代表有權限通路
return nil
将這個InterceptFunc注冊到revel的處理鍊中。
revel.InterceptFunc(checkAuthentication, revel.BEFORE, revel.ALL_CONTROLLERS)
好了,一個簡單的權限控制系統做好了。這裡隻是展現了最基本的概念和建構的思路,掌握了之後,就算面對再複雜的要求也可以通過擴充其中的一個部分去滿足。
另外,在我們的項目山坡網裡,由于authMap定義了超過50個Action,很多時候一旦Action名字更改了就可能導緻權限控制失效,針對這個問題,可以通過下面方法來解決。
authMap之前是用string做key,改成用reflect.Type就好了,注冊的時候這麼寫。
authMap[reflect.TypeOf((*Account.HandleLogin)(nil)] = AnonymousRole
這樣就可以用編譯器幫助檢查錯誤。
最後還得提一句,目前山坡網的Go代碼已經超過兩萬行,且運作良好。revel是個好架構,值得信賴。