一個站點上面最基本都會有三種用戶角色,未登錄用戶、已登錄用戶和管理員。這一次我們就來看看在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是個好框架,值得信賴。