2014年8月8日 星期五

ASP .NET MVC4 WebApi - 不允許路由(Route)跨區域(Area)找Controller

目前專案設定權限是採用Area進行區分,基本規劃為角色 1..n Area

最近在強化權限控管,發現在Area找不到Controller的時候,竟然會去其他的Area找有沒有符合名稱的Controller(如何切換 Area 的 Web Api 路由)

對規劃的權限區分來說,這一個非常嚴重的問題


舉個例子:
(角色=>Area)
人員管理者=>People
薪資管理者=>Salary

王小明有人員管理者這個角色,所以他可以使用People裡的所有Controller, 碰巧王小明知道了Salary的某隻Api路徑可以取得全公司員工的薪資(api/Salary/AllPeople), 就權限管理機制,王小明沒有Salary的Area權限,所以被判定為無權限(401)。

到這邊都很沒問題,一切都很美好...如果這樣就結束,還要這篇幹嘛

偏偏王小明很有求知的精神,想說只要是People就會被放行,那如果把Salary改成People不知道可不可以?
於是原本的路徑(api/Salary/AllPeople)變成了(api/People/AllPeople)。

這時候權限機制判斷王小明是人員管理者,可以訪問People的Area,所以放行讓他通過, 殊不知在People下沒有AllPeople的Controller,但對權限機制來說並不重要,有沒有是ControllerSelector的問題。

這時候AreaHttpControllerSelector出馬了,經過一連串的檢查,發現People下沒有AllPeople的Controller,沒辦法走下去,那就交給老爸(DefaultHttpControllerSelector)去處理吧。

而老爸是不管這些Area的,只要符合ControllerName就好,所以找到了Salary的AllPeople,然後就跟小弟(ApiControllerActionSelector)說,從Salary的AllPeople找符合的Action來執行吧。

接著就會發現王小明引發美麗錯誤的結果,他拿到不該拿的資料了...其實也還好


這樣是不行的,如果Route有接到area的話,就只能從指定的Area下找Controller,沒找到就丟錯誤出去,所以修改一下AreaHttpControllerSelector
...略...
        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

            var areaName = GetAreaName(request);
            if (string.IsNullOrEmpty(areaName))
            {
                return null;
            }

            var type = GetControllerTypeByArea(areaName, controllerName);
            if (type == null)
            {
                //原本回傳null是為了向老爸求救,今天兒子要獨當一面了
                //return null;
                throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
                    string.Format("No route providing a controller name was found to match request URI \"{0}\"", new object[] { request.RequestUri })));
            }

            return new HttpControllerDescriptor(_configuration, controllerName, type);
        }


參考:
如何切換 Area 的 Web Api 路由
DefaultHttpControllerSelector.cs Source Code
ApiControllerActionSelector.cs Source Code