2013-05-07 26 views
4

在我的應用程序中,用戶可以執行很多不同的操作,例如, (書/庫例子只是爲了更清楚;在我的應用程序的操作更加複雜):如何將操作與安全檢查分離?

  • 租一本書
  • 讀一本書
  • 寫一本書
  • 移動本書
  • 查找一本書
  • ...

我的應用程序還包含了一些安全檢查是否會阻止這些操作。 這些檢查沒有直接關係的行動,但是從動作分別表示概念,如:

  • 允許打開一本書或不
  • 允許進行修改或不
  • 不被允許看一本書的所有腦幹或不

目前,不同的動作(租賃,閱讀,...)包含這樣的代碼:

void readBook (Book &book) 
    { 
    if (!checkSecurity (book, OPENBOOK|SEEBOOK)) return; 
    ... 
    } 

void writeBook (Book &book) 
    { 
    if (!checkSecurity (book, MODIFYBOOK)) return; 
    ... 
    } 

這種方法使得以動態方式添加新類型的安全檢查變得非常困難。例如。一個動態的插件可能會增加額外的安全檢查。

我目前正在研究解決辦法是,不同的操作調用,看起來像這樣的接口:

class ISecurityChecker 
    { 
    public: 
     bool isReadBookAllowed() const = 0; 
     bool isWriteBookAllowed() const = 0; 
     ... 
    }; 

複合類,然後實現此接口。該接口的其他實現可以添加到組合中。這樣,應用程序的插件或動態部分可以在需要時添加新的安全檢查。

這種方法也有一些缺點。主要問題是界面變得很大。我將看看我是否可以合併一些接口方法(因爲它們基本上執行相同或類似的操作),但不確定這是否可行。

任何其他替代方案來改善解耦?或者關於如何改進這種設計的建議?

+1

是否存在像「網關」一樣的東西(或有限的一組東西):即,當用戶做某件事情時,它總是通過某種路線行事?例如,在某些應用程序中,用戶操作變爲http請求,因此可能會在查看http請求的單個choke點上應用檢查和規則。你有類似的東西嗎? – 2013-05-07 17:47:23

+0

@Darius。該應用程序是一個大型模擬應用程序,可執行桌面上的所有內容(因爲需要進行大量數學計算才能得到所有數據)。所以沒有中央的http網關。我現在嘗試添加的是一個執行必要檢查的中央功能/類/接口/框架(但不是網關本身),但問題是我最終得到一個大矩陣(N個動作x M個可能的安全檢查)我試圖看看最好的方法是:對每個動作執行M個檢查,或者讓每個檢查都有邏輯來檢查N個動作中的每一個。 – Patrick 2013-05-08 07:33:42

回答

3

有兩種可能的變化,我可以建議...和一個評論。

首先,儘可能推動你的原始檢查鏈。拋出異常來傳達授權錯誤。

void readBook (Book &book) 
{ 
    // readbook doesn't need to perform any checks of it's own. 
    // the more primitive actions will scream and yell if the user 
    // isn't allowed to perform sub-action X. 

    openBook(book); 
    BookData d = getBookData(book); 

    // etc. 
} 

void openBook (Book &book) 
{ 
    if (!checkSecurity (book, OPENBOOK)) 
    throw new SecurityException("User X is not allowed to open this book!"); 

    // etc. 
} 

BookData getBookData (Book &book) 
{ 
    if (!checkSecurity (book, SEEBOOK)) 
    throw new SecurityException("User X is not allowed to read this book's data!"); 

    // etc. 
} 

其次,您的安全操作映射到的實際行動。如果你喜歡,你甚至可以在數據中做到這一點。例如 ...

class Security { 

    // this check get tricky. 
    // if an "real action" isn't listed anywhere, does the user have implicit permission? 
    // (i'm assuming not, for this example.) 
    public static Check(String realAction, Boolean requireAll = true) { 
    Int32 required = 0; 
    Int32 userHas = 0; 

    foreach (KeyValuePair<String, List<String>> pair in Actions) { 
     if (pair.Value.Contains(realAction)) 
     { 
     required++; 
     if (Security.CurrentUser.Actions.Contains(pair.Key)) 
     { 
      userHas++; 
     } 
     } 
    } 

    if (requireAll) 
    { 
     return userHas > 0 && userHas == required; 
    } 
    else 
    { 
     return userHas > 0; 
    } 
    } 

    // hardcoded here, but easily populated from a database or config file 
    public static Dictionary<String, List<String>> Actions { 
    {"OpenBook", new List<String>() { "readBook", "writeBook" }}, 
    {"SeeBook", new List<String>() { "readBook", "writeBook" }} 
    } 

} 

void readBook(Book &book) { 
    if (!Security.Check("readBook")) return false; 
    // etc. 
} 

Check()方法在這裏接受一個requireAll參數,但自己可以很容易地被更新的映射爲「堅持」或存在對他們意味着「偏愛」,「實際行動」。

我的評論:不要過分細節您的安全。一些安全規則意味着其他規則,這些規則本身可能毫無意義。例如,READBOOKWRITEBOOK都意味着OPENBOOK的能力,而OPENBOOK可能本身沒有意義。雖然它可能看起來愚蠢的用戶能夠OPENBOOKWRITEBOOK不喜歡的東西和SEEBOOKCOVERSEEBOOKINSEARCHRESULTS什麼,我建議,在電子書閱讀時,唯一相關權限READBOOK

+1

你的第二種方法看起來我正在嘗試,將調用添加到中央檢查界面,並將操作作爲參數。但是,我不使用字符串,而是使用單獨的方法,然後可以由安全檢查程序執行(或者,如果他們不關心該操作,則不執行)。您的方法很有趣,因爲您在啓動應用程序時通過註冊操作組合和安全檢查來明確定義矩陣。謝謝。 – Patrick 2013-05-08 08:00:55

1

使用命令查詢分離(CQS)方法。總的想法是定義一個命令類是這樣的:

public class Command 
{ 
    public Command(Action<object> act, Func<object, bool> canExecute) 
    { 
     this.act = act; 
     this.canExecute = canExecute; 
    } 
    private Action<object> act; 
    private Func<object, bool> canExecute; 
    public void Execute() 
    { 
     if (CanExecute()) 
     { 
      act(this); 
     } 
     else 
     { 
      throw new InvalidOperationException("Command cannot be executed"); 
     } 
    } 

    public bool CanExecute() 
    { 
     if (this.canExecute != null) 
     { 
      return this.canExecute(this); 
     } 
     else return true; 
    } 
} 

然後在頁面中,您使用Command.Execute()代替普通book.Write/book.Print

用法:

Command readBookCommand = new Command(
    k => 
    { 
     book.Read(); 
    }, 
    l => 
    { 
     CanReadBook(); 
    } 
); 
readBookCommand.Execute(); 

那裏可能會有更好的CQS實施。這只是一個簡單的例子。

相關問題