ACL và Bộ điều khiển
Trước hết: Đây là những thứ / lớp khác nhau thường xuyên nhất. Khi bạn chỉ trích mã bộ điều khiển mẫu, nó đặt cả hai lại với nhau - rõ ràng là quá chặt chẽ.
tereško đã vạch ra một cách làm thế nào để bạn có thể phân tách điều này nhiều hơn với mẫu trang trí.
Trước tiên, tôi sẽ quay lại một bước để tìm kiếm vấn đề ban đầu mà bạn đang gặp phải và thảo luận về vấn đề đó một chút sau đó.
Một mặt, bạn muốn có các bộ điều khiển chỉ thực hiện công việc mà chúng được lệnh (lệnh hoặc hành động, hãy gọi nó là lệnh).
Mặt khác, bạn muốn có thể đưa ACL vào ứng dụng của mình. Lĩnh vực hoạt động của các ACL này phải - nếu tôi hiểu đúng câu hỏi của bạn - để kiểm soát quyền truy cập vào các lệnh nhất định của các ứng dụng của bạn.
Do đó, loại kiểm soát truy cập này cần một thứ gì đó khác kết hợp hai điều này lại với nhau. Dựa trên ngữ cảnh mà một lệnh được thực thi, ACL sẽ khởi động và các quyết định cần được thực hiện liệu một lệnh cụ thể có thể được thực thi bởi một chủ thể cụ thể hay không (ví dụ: người dùng).
Hãy tóm tắt đến thời điểm này những gì chúng ta có:
- Chỉ huy
- ACL
- Người sử dụng
Thành phần ACL là trung tâm ở đây: Nó cần biết ít nhất một điều gì đó về lệnh (để xác định chính xác lệnh) và nó cần có khả năng xác định người dùng. Người dùng thường dễ dàng được xác định bằng một ID duy nhất. Nhưng thường trong các ứng dụng web có những người dùng hoàn toàn không được xác định, thường được gọi là khách, ẩn danh, mọi người, v.v. Đối với ví dụ này, chúng tôi giả định rằng ACL có thể sử dụng một đối tượng người dùng và gói gọn các chi tiết này lại. Đối tượng người dùng bị ràng buộc với đối tượng yêu cầu ứng dụng và ACL có thể sử dụng nó.
Điều gì về xác định một lệnh? Cách diễn giải của bạn về mẫu MVC cho thấy rằng một lệnh là kết hợp của tên lớp và tên phương thức. Nếu chúng ta quan sát kỹ hơn sẽ có các đối số (tham số) chẵn cho một lệnh. Vì vậy, nó hợp lệ để hỏi điều gì xác định chính xác một lệnh? Tên lớp, tên phương thức, số lượng hoặc tên của các đối số, thậm chí là dữ liệu bên trong bất kỳ đối số nào hoặc hỗn hợp của tất cả những thứ này?
Tùy thuộc vào mức độ chi tiết mà bạn cần để xác định một lệnh trong ACL'ing của mình, điều này có thể khác nhau rất nhiều. Đối với ví dụ, hãy giữ nó đơn giản và chỉ định rằng một lệnh được xác định bằng tên lớp và tên phương thức.
Vì vậy, bối cảnh của ba phần này (ACL, Command và User) thuộc về nhau như thế nào bây giờ rõ ràng hơn.
Chúng tôi có thể nói, với sự phù hợp ACL tưởng tượng, chúng tôi đã có thể làm như sau:
$acl->commandAllowedForUser($command, $user);
Chỉ cần xem điều gì đang xảy ra ở đây: Bằng cách làm cho cả lệnh và người dùng đều có thể nhận dạng được, ACL có thể thực hiện nó. Công việc của ACL không liên quan đến công việc của cả đối tượng người dùng và lệnh cụ thể.
Chỉ thiếu một bộ phận, cái này không thể sống trong không khí. Và nó không. Vì vậy, bạn cần xác định vị trí mà kiểm soát truy cập cần khởi động. Hãy xem điều gì xảy ra trong một ứng dụng web tiêu chuẩn:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Để xác định vị trí đó, chúng tôi biết nó phải có trước khi lệnh cụ thể được thực thi, vì vậy chúng tôi có thể giảm danh sách đó và chỉ cần xem xét các địa điểm (tiềm năng) sau:
User -> Browser -> Request (HTTP)
-> Request (Command)
Tại một số điểm trong ứng dụng của bạn, bạn biết rằng một người dùng cụ thể đã yêu cầu thực hiện một lệnh cụ thể. Bạn đã thực hiện một số loại ACL ở đây: Nếu người dùng yêu cầu một lệnh không tồn tại, bạn không cho phép lệnh đó thực thi. Vì vậy, bất cứ nơi nào xảy ra trong ứng dụng của bạn có thể là một nơi tốt để thêm các kiểm tra ACL "thực":
Lệnh đã được định vị và chúng tôi có thể tạo ra nhận dạng của nó để ACL có thể xử lý nó. Trong trường hợp lệnh không được phép đối với người dùng, lệnh sẽ không được thực hiện (hành động). Có thể CommandNotAllowedResponse
thay vì CommandNotFoundResponse
cho trường hợp, một yêu cầu không thể được giải quyết bằng một lệnh cụ thể.
Nơi ánh xạ một HTTPRequest cụ thể được ánh xạ vào một lệnh thường được gọi là Định tuyến . Vì Định tuyến đã có công việc định vị một lệnh, tại sao không mở rộng nó để kiểm tra xem lệnh đó có thực sự được phép trên mỗi ACL không? Ví dụ như bằng cách mở rộng Router
đến một router biết ACL: RouterACL
. Nếu bộ định tuyến của bạn chưa biết User
, thì Router
không phải là nơi thích hợp, bởi vì để ACL hoạt động không chỉ có lệnh mà còn phải xác định được người dùng. Vì vậy, địa điểm này có thể khác nhau, nhưng tôi chắc chắn rằng bạn có thể dễ dàng xác định vị trí bạn cần mở rộng, vì đó là nơi đáp ứng đầy đủ yêu cầu của người dùng và lệnh:
User -> Browser -> Request (HTTP)
-> Request (Command)
Người dùng có sẵn ngay từ đầu, Lệnh đầu tiên với Request(Command)
.
Vì vậy, thay vì đặt các kiểm tra ACL của bạn bên trong triển khai cụ thể của mỗi lệnh, bạn đặt nó trước nó. Bạn không cần bất kỳ mẫu nặng nề, phép thuật hay bất cứ điều gì, ACL thực hiện công việc của nó, người dùng thực hiện công việc và đặc biệt là lệnh thực hiện công việc của nó: Chỉ là lệnh, không có gì khác. Lệnh không quan tâm đến việc liệu các vai trò có áp dụng cho nó hay không, nếu nó được bảo vệ ở đâu đó hay không.
Vậy nên hãy cứ để những thứ không thuộc về nhau. Sử dụng bản ghi lại một chút Nguyên tắc trách nhiệm duy nhất (SRP) : Chỉ nên có một lý do để thay đổi lệnh - bởi vì lệnh đã thay đổi. Không phải vì bây giờ bạn giới thiệu ACL'ing trong ứng dụng của mình. Không phải vì bạn chuyển đổi đối tượng Người dùng. Không phải vì bạn di chuyển từ giao diện HTTP / HTML sang SOAP hoặc giao diện dòng lệnh.
ACL trong trường hợp của bạn kiểm soát quyền truy cập vào một lệnh, không phải chính lệnh đó.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(khác, hiển thị "Bạn không cần phải truy cập vào của thành viên này hồ sơ" hoặc một cái gì đó giống như là tôi không có được nó?.