การตรวจสอบการอนุญาตของผู้ใช้จะเกิดขึ้นที่ใดและใน MVC และโดยใคร


26

ควรตรวจสอบการอนุญาตของผู้ใช้เกิดขึ้นในรุ่นหรือคอนโทรลเลอร์ และใครควรจัดการกับการตรวจสอบการอนุญาตวัตถุผู้ใช้หรือผู้ช่วยการจัดการบางอย่าง?

มันควรจะเกิดขึ้นที่ไหน?

การตรวจสอบในคอนโทรลเลอร์:

class MyController {
  void performSomeAction() {
    if (user.hasRightPermissions()) {
      model.someAction();
    }
  }
  ...

การมีการตรวจสอบในคอนโทรลเลอร์ช่วยให้การดำเนินการเป็นแบบอย่างง่ายดังนั้นเราจึงสามารถเก็บตรรกะทั้งหมดไว้กับตัวควบคุมได้

ตรวจสอบในรูปแบบ:

class MyModel {
  void someAction() {
    if (user.hasRightPermissions()) {
      ...
    }
  }
  ...

โดยการใส่เช็คเข้าไปในโมเดลเราทำให้โมเดลนั้นมีความซับซ้อน แต่ก็ต้องแน่ใจว่าเราไม่อนุญาตให้ผู้ใช้ทำสิ่งที่พวกเขาไม่ควรจะอยู่ในคอนโทรลเลอร์

แล้วใครล่ะ

เมื่อเราตัดสินในสถานที่แล้วใครควรทำการตรวจสอบ? ผู้ใช้งาน?

Class User {
  bool hasPermissions(int permissionMask) {
    ...
  }
  ...

แต่มันไม่ใช่ความรับผิดชอบของผู้ใช้ที่จะรู้ว่าเขาหรือเธอสามารถทำอะไรได้บ้างดังนั้นบางทีผู้ช่วยบางคน?

Class UserManagement {
  bool hasPermissions(User user, int permissionMask) {
    ...
  }
  ...

ฉันรู้ว่ามันเป็นเรื่องธรรมดาที่จะถามคำถามเดียวในคำถาม แต่ฉันคิดว่าคำถามเหล่านี้สามารถตอบได้ดี

คำตอบ:


20

ตามปกติ "มันขึ้นอยู่กับ"

  • การตรวจสอบการอนุญาตจะใช้ งานได้ทุกที่ที่สะดวกในการติดตั้ง
  • แต่ถ้าคุณถามคำถามทางเทคนิคคำตอบอาจเป็น 'ใส่เช็คเข้าไปในวัตถุที่เป็นเจ้าของข้อมูลที่จำเป็นในการตรวจสอบ' (ซึ่งอาจเป็นตัวควบคุม)
  • แต่ถ้าคุณกำลังถามคำถามปรัชญาผมขอแนะนำให้คำตอบอื่น: ไม่แสดงการกระทำของผู้ใช้ว่าพวกเขาจะไม่ได้รับอนุญาตให้ดำเนินการ

ดังนั้นในกรณีหลังคุณอาจมีการตรวจสอบสิทธิ์ในคอนโทรลเลอร์ที่ใช้งานเป็นคุณสมบัติบูลีนและผูกคุณสมบัตินั้นกับคุณสมบัติที่มองเห็นได้ของปุ่มหรือแผงในส่วนต่อประสานผู้ใช้ที่ควบคุมการทำงาน

ในฐานะผู้ใช้มันเป็นเรื่องน่าผิดหวังที่เห็นปุ่มสำหรับการกระทำที่ฉันไม่สามารถทำได้ รู้สึกเหมือนฉันถูกทิ้งให้สนุก;)


แอปพลิเคชันของเราใช้สถานการณ์ที่สามโดยมีข้อยกเว้นว่าเราไม่ซ่อนการควบคุมเราปิดการใช้งาน น่าเสียดายที่มันทำใน Winforms code-behind ดังนั้นมันจึงไม่เกี่ยวข้องกับคำถาม OP
Dave Nay

11
"มันน่าผิดหวังที่เห็นปุ่มสำหรับการกระทำที่ฉันไม่สามารถทำได้" -> ลองโพสต์ข้อความของคุณเอง :)
Rowan Freeman

5
ไม่เพียงพอที่จะซ่อนปุ่มสำหรับการกระทำที่ผู้ใช้ไม่สามารถทำได้เซิร์ฟเวอร์ต้องตรวจสอบทุกคำขอเพื่อขออนุญาต รายการหัวข้อย่อยที่สามไม่ใช่ "คำตอบอื่น" เป็นสิ่งที่ต้องทำเพิ่มเติมจากการตรวจสอบสิทธิ์ฝั่งเซิร์ฟเวอร์
Flimm

@Flimm เห็นด้วยถ้าคำขอได้รับการจัดการโดยเซิร์ฟเวอร์ คำถามเฉพาะเกี่ยวกับคลาส Controller
Steven A. Lowe

7

ความปลอดภัยเป็นเรื่องที่ต้องคำนึงถึงเป็นพิเศษ สิ่งต่อไปนี้เป็นตัวอย่างสำหรับ MVC แต่แนวคิดนี้ใช้กับสถาปัตยกรรมและ / หรือรูปแบบอื่น ๆ คุณต้องระบุจุดบังคับใช้

มันควรจะเกิดขึ้นที่ไหน?

มุมมองอาจมีองค์ประกอบ UI (วิดเจ็ตปุ่มเมนู ฯลฯ ) ที่จำเป็นต้องแสดงหรือไม่สำหรับผู้ใช้บางรายขึ้นอยู่กับการอนุญาตของพวกเขา นี่อาจเป็นความรับผิดชอบของเอ็นจิ้นการดูเนื่องจากคุณไม่ต้องการให้ทุกมุมมองจัดการสิ่งนี้ด้วยตนเอง ขึ้นอยู่กับประเภทขององค์ประกอบที่คุณกำลังอนุญาตให้คุณย้ายความรับผิดชอบนี้ในที่อื่น ตัวอย่างเช่นคิดเมนูที่ต้องแสดงบางรายการและบางรายการไม่แสดง ไอเท็มสามารถนำไปใช้เป็นรายการบางรายการและกรองรายการตามสิทธิ์แล้วส่งต่อไปยังมุมมอง

ผู้ควบคุมตอบสนองต่อการร้องขอดังนั้นหากผู้ใช้ไม่มีสิทธิ์ในการดำเนินการควรทำการตรวจสอบก่อนที่จะมีการเรียกใช้การดำเนินการย้ายความรับผิดชอบไปยังผู้กระทำการกระทำแทนที่จะเก็บไว้ในตัวควบคุม นี่เป็นข้อดีของการทำให้คอนโทรลเลอร์ของคุณสะอาดและหากมีอะไรเปลี่ยนแปลงในการอนุญาตคุณไม่จำเป็นต้องกรองผ่านคอนโทรลเลอร์เพื่อใช้การเปลี่ยนแปลงเหล่านั้น

ทรัพยากรจะแสดงตามการอนุญาต โดยปกติจะทำในระดับฐานข้อมูลเนื่องจากคุณไม่ต้องการดึงทุกอย่างออกจากฐานข้อมูลแล้วใช้สิทธิ์

อย่างที่คุณเห็นขึ้นอยู่กับสิ่งที่คุณต้องการอนุญาตมีสถานที่ต่าง ๆ ที่ควรทำ เป้าหมายคือการไม่สร้างความรำคาญให้มากที่สุดเท่าที่จะทำได้ดังนั้นเมื่อนโยบายความปลอดภัยของคุณเปลี่ยนไปคุณสามารถนำไปใช้ได้ง่ายโดยไม่ต้องเปลี่ยนรหัสแอปพลิเคชันของคุณ สิ่งนี้อาจไม่ถูกต้องสำหรับแอปพลิเคชันขนาดเล็กซึ่งชุดการอนุญาตมีขนาดค่อนข้างเล็กและไม่เปลี่ยนแปลงบ่อยนัก ในแอพพลิเคชั่นระดับองค์กรเรื่องราวนั้นแตกต่างกันมาก

ใครควรทำ

เห็นได้ชัดว่าไม่ใช่รุ่น แต่ละชั้นควรมีจุดบังคับใช้ที่จัดการการอนุญาต ข้อความตัวเอียงด้านบนเน้นจุดบังคับใช้ที่เป็นไปได้สำหรับแต่ละระดับ

มีลักษณะที่XACML คุณไม่ต้องใช้มันอย่างที่เป็นอยู่ แต่มันจะบอกทิศทางที่คุณสามารถทำตามได้


นี่คือคำตอบที่ดีที่สุด ด้วยเหตุผลบางอย่างท็อปเท็นและอื่น ๆ จัดการกับความแตกต่างระหว่างคอนโทรลเลอร์และมุมมองหรือมุมมองและโมเดลซึ่งไม่ใช่สิ่งที่ OP ขอ ขอบคุณ!
redFur

1

ฉันใช้รูปแบบดังต่อไปนี้ เป็นมูลค่าที่จะกล่าวว่าการตรวจสอบสิทธิ์ของผู้ใช้ส่วนใหญ่สามารถแบ่งออกเป็นสองกรณีทั่วไป:

  • ผู้ใช้เข้าถึงการกระทำของตัวควบคุมตามบทบาทของผู้ใช้โดยไม่ตรวจสอบการกระทำของพารามิเตอร์ที่ถูกเรียกด้วย
  • การเข้าถึงของผู้ใช้กับโมเดลโดยอิงตามตรรกะหรือความสัมพันธ์ระหว่างผู้ใช้และโมเดลเฉพาะ

การเข้าถึงการกระทำของคอนโทรลเลอร์โดยไม่ตรวจสอบคุณสมบัติมักใช้ในกรอบงาน MVC สิ่งนี้ง่ายมาก: คุณกำหนดกฎผู้ใช้ของคุณมีบทบาท คุณเพียงตรวจสอบว่าผู้ใช้ได้รับอนุญาตให้ดำเนินการค้นหาบทบาทในกฎ

การเข้าถึงของผู้ใช้ไปยังรุ่นเฉพาะควรกำหนดไว้ในแบบจำลอง (นักแสดงเป็นคลาสผู้ใช้พื้นฐานสมมติว่าอาจเป็นลูกค้าผู้ขายหรือแขก)

interface ICheckAccess
{
    public function checkAccess(Actor $actor, $role);
}

class SomeModel implements ICheckAccess
{
    public function checkAccess(Actor $actor, $role)
    {
        // Your permissions logic can be as sophisticated as you want.
    }
}

การวางตรรกะนั้นไว้ในแบบจำลองจะก่อให้เกิดกำไร วิธีการตรวจสอบการเข้าถึงสามารถสืบทอดคุณไม่จำเป็นต้องสร้างชั้นเรียนพิเศษใด ๆ คุณสามารถใช้ประโยชน์จาก OOP ทั่วไป

ต่อไปเพื่อลดความซับซ้อนของการตรวจสอบการเข้าถึงเราใช้สมมติฐานบางอย่างที่เกือบจะนำมาใช้แล้วสำหรับความเรียบง่ายและสไตล์ที่ดี:

  • โดยปกติแล้วคอนโทรลเลอร์จะเกี่ยวข้องกับคลาสโมเดลบางตัว
  • การกระทำที่มีการตรวจสอบสำหรับการเข้าถึงใช้รหัสรุ่นเดียวเป็นพารามิเตอร์
  • พารามิเตอร์นี้สามารถเข้าถึงได้อย่างสม่ำเสมอจากวิธีการของคลาสคอนโทรลเลอร์ฐาน;
  • การกระทำจะถูกวางไว้ในตัวควบคุมที่สอดคล้องกับรูปแบบที่การกระทำ id

ด้วยสมมติฐานเหล่านี้การกระทำที่ใช้รหัสประจำรุ่นสามารถเชื่อมโยงกับอินสแตนซ์ของโมเดลเฉพาะได้ ในความเป็นจริงการกระทำส่วนใหญ่สามารถเปลี่ยนและย้ายได้อย่างง่ายดายเพื่อให้เหมาะสมกับสมมติฐานที่กล่าวข้างต้น

จากนั้นคลาสคอนโทรลเลอร์นามธรรมบางคลาสควรถูกกำหนดและสืบทอด

abstract class ModelController
{
    // Retrieve model from database using id from action parameter.
    public abstract function loadModel($id);

    // Returns rules for user role to pass to SomeModel::checkAccess()
    // Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
    public abstract function modelRules();

    public abstract fucntion getIdParameter();

    public function filterModelAccess()
    {
        $id = $this->getIdParameter();
        if(!$this->checkModelAccess($id))
            throw new HttpException(403);
    }

    public function checkModelAccess($id)
    {
        $model = $this->loadModel($id);
        $actor = My::app()->getActor();
        $rules = $this->modelRules();
        $role = $rules[My::app()->getActionName()];
        return $model->chechAccess($actor, $role);
    }
}

คุณสามารถเรียกใช้ SomeController :: checkModelAccess ($ id) วิธีการเมื่อคุณสร้างเมนูของคุณและตัดสินใจว่าจะแสดงลิงค์


ฉันขอโทษสำหรับ PHP
George Sovetov

1

ทั้งใน Model และ View

ในมุมมอง - เนื่องจาก UI ไม่ควรแสดงองค์ประกอบ UI ที่ จำกัด เฉพาะผู้ใช้ปัจจุบัน

(เช่นพูดว่าปุ่ม "ลบ" ควรปรากฏต่อผู้ที่มีสิทธิ์ที่เหมาะสม)

ในรูปแบบ - เนื่องจากแอปของคุณอาจมี API บางประเภทใช่ไหม API ต้องตรวจสอบการอนุญาตเช่นกันและอาจใช้โมเดลอีกครั้ง

(เช่นพูดว่าคุณมีปุ่ม "ลบ" ใน UI และวิธี "http: / server / API / DeleteEntry / 123" ในเวลาเดียวกัน


ทำไมคุณถึงเลือกรุ่นมากกว่าคอนโทรลเลอร์
Flimm

ไม่แน่ใจว่าทำไมดูรุ่นและไม่อยู่ในตัวควบคุมที่ส่วนใหญ่จะทำ
รองประธาน

@VP ควบคุมไม่มีอำนาจเพื่อแสดง / ซ่อนองค์ประกอบ UI (นอกเหนือผ่านบูล-var เพื่อดู)
Jitbit

ฉันไม่รู้ว่าปกติทุกที่ในเลเยอร์คอนโทรลเลอร์นั่นเป็นสาเหตุที่ฉันอยากรู้
รองประธาน

0

MVC เป็นรูปแบบการนำเสนอ มุมมองและตัวควบคุมดังกล่าวควรมีความรับผิดชอบเกี่ยวกับงานนำเสนอเท่านั้น สิทธิ์บางอย่างใช้กับงานนำเสนอเช่นโหมดผู้เชี่ยวชาญคุณสมบัติ UI ทดลองหรือการออกแบบที่แตกต่างกัน MVC-คอนโทรลเลอร์สามารถจัดการได้

การอนุญาตประเภทอื่น ๆ อีกมากมายเกี่ยวข้องกับหลายเลเยอร์ของแอปพลิเคชัน ตัวอย่างเช่นหากคุณต้องการมีผู้ใช้ที่สามารถดูข้อมูลได้เท่านั้นและไม่เปลี่ยนแปลงสิ่งต่าง ๆ :

  • เลเยอร์การนำเสนอจะต้องซ่อนคุณสมบัติการแก้ไข
  • หากคุณสมบัติการแก้ไขถูกเรียกใช้ต่อไปสิ่งนี้สามารถตรวจพบได้ / (โดยส่วนเฉพาะของแอปพลิเคชันของชั้นธุรกิจไม่ใช่ส่วนเฉพาะของโดเมน - TrainEditor ไม่ใช่ Train) และอาจทำให้เกิดข้อยกเว้น
  • ชั้นการเข้าถึงข้อมูลยังสามารถตรวจสอบการเขียนได้เช่นกัน แต่สำหรับการอนุญาตที่ซับซ้อนมากขึ้นซึ่งต้องการความรู้อย่างรวดเร็วของเลเยอร์ธุรกิจเป็นความคิดที่ดี

มีการทำซ้ำในวิธีการนี้ แต่เนื่องจากงานนำเสนอมักจะมีความผันผวนเราสามารถสร้างกรณีที่ดีสำหรับการตรวจสอบการอนุญาตในส่วนที่มีความเสถียรมากกว่าปกติของแอปพลิเคชันแม้ว่านั่นหมายถึงการตรวจสอบซ้ำซ้อนในกรณีที่เลเยอร์การนำเสนอทำงานได้ตามที่ต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.