想請問依下各位大大的經驗~ 一般來說我們在開發Rest API 最基本會注意到 Authenication (你是誰) & Authority (你有沒有權限) 所以在API Level 我們可以設定誰有權限可以Call 這個API (如查詢用戶資訊) 但是有權限以後又想限制這人只能只能查詢自己相關的資料 那各位會如何設計呢? 下SQL時query 加上跟自己相關的where 條件?
後台權限我做的不多,沒接觸過大型企業用的系統。我自己的經驗就是你說的在 where 上加限制。(不過這本來就會下吧,不然怎麼查自己的相關記錄?)
我以前犯過一個錯誤,就是 authentication/authorization 都加了,但少做了個檢查:
// Rest API 層,處理 authentication 這塊
in getBalance(String username, String authenticationToken) {
checkAuthentication(authenticationToken);
return service.getBalance(username);
}
// 後台 service 層,有檢查這個用戶有沒有權限可拿 balance
int getBalance(String username) {
checkUserCanGetBalance(username);
return database.queryBlance(username);
}
上面的範例,Rest 層有驗 authentication,然後背後的 service 層有驗 該 user 有沒有取得 balance 的權限。而那個 database 的 query 就是下 where 條件。
我犯的錯是,我沒有檢查傳進的 username 和 authenticationToken 是同個人,所以 一個可通過驗證的人,變成可以傳不同 username 進來,取得所有人的 balance。
這個白吃的錯誤給大家借鏡...
正常來說where 加上限制,加限制是查詢使用者本身相關的資料 但是以上面的例子就是我有authentication 過了,然後我也有權限拿資料(access balance這個table) 但是如果使用者傳的是不同的username進來就GG了... 而且query越複雜就越麻煩...Orz.. 比如說這個使用者管理n間公司,n間公司又有m台機器 如果要對某台機器的資料設定,那要先確認這台機器是不是屬於這個使用這可以管理的 ,感覺就會把簡單的邏輯越寫越複雜...不知道有沒有好的design pattern
我也不知道很複雜的狀況下,有什麼簡單的寫法。有可能這件事本來就是這麼難。
我犯的錯誤,我後來用了兩種修正法:
//第一種,在 rest 層加上 annotation
int getBalance(
@TargetUser String username,
@Auth String authenticationToken) {
這個寫法是我在 legacy 系統下加的,設計個 interceptor 之類的會攔截 method call,看到有 @TargetUser
和 @Auth
這樣的 annotation 就會強制檢查是不是同一個人。然後會有另一個 unit test 去掃所有的 rest API,確定所有的 rest API 都有設定 security 相關的 annotation。
當然這是 legacy 系統,所以我不能改太多 signature ,只好用外加的方式處理。
後來,kaif 這個新系統,我就換了另一個方式來做:
int getBalance(String authenticationToken) {
Auth auth = checkAuthentication(authenticationToken);
return service.getBalance(auth);
}
// 後台 service 層, 參數是 type safe 的 Auth
int getBalance(Auth auth) {
checkUserCanGetBalance(auth);
return database.queryBlance(auth.getUsername());
}
第二種做法,你會看到我改用 type safe 的觀念來處理 authorization。service 層根本看不到 username 這種
純字串的值了,它被設計成只能傳 Auth ,這個 已經是個驗證過的人
的物件。這樣的設計自然去掉了之前的問題。
以後重用這個 service.getBalance(auth) 的人,它沒有別的選擇,只能想辦法生個 Auth 物件才能呼叫。
我想這兩個做法都是個方向,我會傾向用 type safe 的方式保護 API,讓授權這個隱含的性質突顯出來。 type safe 的做法,也許更複雜一點的會變這樣吧:
// service getBalance 的參數限制的更嚴格:
int getBalance(CompanyOwner companyOwner) {...}
// 而 companyOwner 則是推導自 Auth
CompanyOwner verifyOwnership(Auth auth, String company) {
return database.queryCompany(
auth.username, company);
}