2013-04-09 89 views
2

考慮下面的代碼:如何將授權邏輯與控制器操作分開?

public class BackupsController : ApiController 
{ 
    private readonly IApiContext context; 
    private readonly IBackupService backupService; 

    public BackupsController(IApiContext context, IBackupService backupService) 
    { 
     this.context = context; 
     this.backupService = backupService; 
    } 

    public HttpResponseMessage Get(Guid id) 
    { 
     if (id == Guid.Empty) 
     { 
      throw new HttpResponseException(HttpStatusCode.BadRequest); 
     } 

     IBackupView backup = backupService.Get(id); 

     if (backup == null) 
     { 
      return Request.CreateErrorResponse(HttpStatusCode.NotFound, String.Format("BackupId '{0}' not found.", id)); 
     } 

     if (!IsAuthorizedForBackup(backup)) 
     { 
      throw new HttpResponseException(HttpStatusCode.Forbidden); 
     } 

     return Request.CreateResponse(HttpStatusCode.OK, backup); 
    } 

    private bool IsAuthorizedForBackup(IBackupView backup) 
    { 
     if (context.Principal.IsInRole(MembershipRole.Admin)) 
     { 
      return true; 
     } 

     if (context.Principal.AllowDataSharing && backup.UserId == context.Principal.UserId) 
     { 
      return true; 
     } 

     if (backup.UserId == context.Principal.UserId && backup.Device.Uuid == context.DeviceUuid) 
     { 
      return true; 
     } 

     return false; 
    } 
} 

是否有意義幾乎所有的提取方法體進入授權過濾器?沒有兩次檢索備份,我沒有辦法做到這一點。

你會如何從控制器動作分離授權的關注?

回答

3

爲了從控制器邏輯單獨的安全邏輯我更喜歡使用HTTP頭攜帶瀏覽器和控制器之間的安全令牌和自定義AuthorizeAttribute

例如檢查標頭值;

jQuery中的Ajax功能beforeSend功能設置的安全令牌(這是以前從服務器取,見下文)

beforeSend: function (xhr) { 
    xhr.setRequestHeader('requestToken', model.requestToken); 
} 

檢查令牌在自定義AuthorizeAttribute

public class AuthAttribute : AuthorizeAttribute 
{ 
    public override void OnAuthorization(HttpActionContext actionContext) 
    { 
     var token = HttpContext.Current.Request.Headers["requestToken"]; 
     // Do the authorization based on token 
    } 
} 

裝點控制器,其行爲需要授權,使用自定義[Auth]屬性,如:

[Auth] 
public class SomeController : ApiController 

我們可以再次使用HTTP頭

HttpContext.Current.Response.Headers["requestToken"] = Guid.NewGuid(); 

而在客戶端,你可以將其存儲在JQuery的AJAX功能的成功,函數在請求

success: function (res, status, xhr) { 
    model.requestToken = xhr.getResponseHeader('requestToken'); 
} 

送回發送新的令牌返回到客戶端這可能不是處理你的情況十分完美,但主要思想是在HTTP頭中的自定義承載(最好是加密的)安全數據處理和安全的事情AuthorizeAttribute

+0

嘿穆罕默德,感謝您抽出時間來回答。我非常瞭解如何利用自定義的AuthorizeAttributes。在我的具體示例中,我更具體地想了解是否可以將授權檢查與控制器操作分開。請注意授權檢查如何根據給定傳遞給操作的參數的服務調用。 – 2013-04-09 14:27:40

+0

我明白了。如果您不想檢索備份對象兩次,仍然可以將認證邏輯抽取到AuthAttribute中,並且如果授權通過,請將備份對象放入可訪問的某個字典(Session,HttpContext.Current.Items,CallContext或自定義對象)中從控制器。然後從該字典中檢索相同的備份對象。但是這可能會降低可維護性,任何在控制器中編寫代碼的人都應該知道所需的備份對象位於某個全局字典中的某處。 – 2013-04-10 12:54:41

0

你所要求的在技術上是可行的。假設你實現了一個動作過濾器,並且在重寫的OnActionExecuted中有一些邏輯來將狀態代碼設置爲Forbidden。我沒有這樣做,這只是對你的探索可行性的建議。 OnActionExecuted在操作方法之後運行,並且可以訪問備份。

另一個更好的選擇是使用權利要求基於身份和實現一個子類ClaimsAuthorizationManager的。 CheckAccess(AuthorizationContext)同時引入行動聲明和資源聲明。與備份相關的屬性可以作爲資源聲明傳遞。