定製可訪問資料

天笑發表於2017-03-08

定製可訪問資料

除了限制使用者可以訪問哪些表和欄位,還常會遇到一類需求是限制使用者只能訪問自己的資料。

[任務]

使用者登入後,可以新增訂單、檢視自己的訂單。 我們在設計文件中設計介面如下:

新增訂單
Ordr.add()(amount) -> id

檢視訂單
Ordr.query() -> tbl(id, userId, status, amount)
Ordr.get(id) -> { 同query介面欄位...}

應用邏輯

- 許可權:AUTH_USER
- 使用者只能新增(add)、檢視(get/query)訂單,不可修改(set)、刪除(del)訂單
- 使用者只能檢視(get/query)屬於自己的訂單。
- 使用者在新增訂單時,必須設定amount欄位,不可設定userId, status這些欄位。
  後端將userId欄位自動設定為該使用者編號,status欄位自動設定為"CR"(已建立)

上面介面原型描述中,get介面用"..."省略了詳細的返回欄位,因為返回物件的欄位與query介面是一樣的,兩者寫清楚一個即可。

實現物件型介面,我們一般寫到檔案php/api_objects.php中:

class AC1_Ordr extends AccessControl
{
    protected $allowedAc = ["get", "query", "add"];
    protected $requiredFields = ["amount"];
    protected $readonlyFields = ["status", "userId"];

    // get/query介面會回撥
    protected function onQuery()
    {
        $userId = $_SESSION["uid"];
        $this->addCond("t0.userId={$userId}");
    }

    // add/set介面會回撥
    protected function onValidate()
    {
        if ($this->ac == "add") {
            $userId = $_SESSION["uid"];
            $_POST["userId"] = $userId;
            $_POST["status"] = "CR";
        }
    }
}
  • 在get/query操作中,會回撥onQuery函式,在這裡我們用addCond新增了一條限制:使用者只能檢視到自己的訂單。 addCond的引數可理解為SQL語句中WHERE子句的片段;欄位用"t0.userId"來表示,其中"t0"表示當前操作表"Ordr"的別名(alias)。後面會講到聯合查詢(join)其它表,就可能使用其它表的別名。

  • add/set操作會回撥onValidate函式(本例中$allowedAc中未定義"set",因而不會有"set"操作過來)。在這個回撥中常常設定$_POST[欄位名]來自動完成一些欄位。

  • 注意$_SESSION["uid"]變數是在使用者登入成功後設定的,由於"AC1"類是使用者登入後使用的,所以必能取到該變數。

[任務]

我們把需求稍擴充套件一下,現在允許set/del操作,即使用者可以更改和刪除自己的訂單。

可以這樣實現:

class AC1_Ordr extends AccessControl
{
    protected $allowedAc = ["get", "query", "add", "set", "del"];
    ...
    // get/set/del介面會回撥
    protected function onValidateId()
    {
        $uid = $_SESSION["uid"];
        $id = mparam("id");
        $rv = queryOne("SELECT id FROM Ordr WHERE id={$id} AND userId={$uid}");
        if ($rv === false)
            throw new MyException(E_FORBIDDEN, "not your order");
    }
}

可通過onValidateId回撥來限制get/set/del操作時,只允許訪問自己的訂單。

函式mparam用來取必傳引數(m表示mandatory)。 函式queryOne用來查詢首行資料,如果查詢只有一列,則返回首行首列資料,但如果查詢不到資料,就返回false. 這裡如果返回false,既可能是訂單id不存在,也可能是雖然存在但是是別人的訂單,簡單處理,我們都返回一個E_FORBIDDEN異常。

框架對異常會自動處理,一般不用特別再檢查資料庫操作失敗之類的異常。如果返回錯誤物件,可丟擲MyException異常:

throw new MyException(E_FORBIDDEN);

錯誤碼"E_FORBIDDEN"表示沒有許可權,不允許操作;常用的其它錯誤碼還有"E_PARAM",表示引數錯誤。

MyException的第二個引數是內部除錯資訊,第三個引數是對使用者友好的報錯資訊,比如:

throw new MyException(E_FORBIDDEN, "order id {$id} does not belong to user {$uid}", "不是你的訂單,不可操作");

相關文章