IngramChen 積分 5 編輯於

我也不知道很複雜的狀況下,有什麼簡單的寫法。有可能這件事本來就是這麼難。

我犯的錯誤,我後來用了兩種修正法:

//第一種,在 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);
}
這是文章的子討論串,你可以回到上層查看所有討論和文章