Lưu trữ các mục menu với quyền của người dùng


11

Tôi đang tạo một hệ thống menu trong PHP và MySQL. Tôi sẽ có một vài menu khác nhau và mỗi menu sẽ có một bộ các mục menu được kết nối với nó.

Trên trang web, tôi cũng có quyền người dùng khác nhau, một số người dùng có thể thấy tất cả các mục menu và một số mục bị ẩn khỏi một số người dùng. Tôi tò mò về cách tôi có thể xử lý các quyền theo cách rõ ràng sẽ cho phép dễ dàng thêm nhiều loại người dùng trong tương lai.

Những gì tôi có cho đến nay là một cái gì đó như thế này:

-------------------
|Menus
-------------------
|id| |display name|
-------------------

----------------------------------------------------------
|Menu items
----------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| |permission|
----------------------------------------------------------

Tôi nghĩ rằng permissioncột có thể là một chuỗi được phân tách bằng dấu phẩy mà tôi có thể khớp với id quyền của người dùng hiện tại. Nó cũng có thể là một tham chiếu đến một số bảng khác xác định tất cả các kết hợp có thể có của các quyền hiện có.

Một giải pháp cũng có thể đơn giản là lưu trữ nhiều mục menu trong đó sự khác biệt duy nhất là sự cho phép mặc dù điều này sẽ dẫn đến lưu trữ trùng lặp và có lẽ là một vấn đề khó khăn khi quản trị.

Tôi rất muốn nghe một số suy nghĩ về cách cấu trúc này và những gì có thể được coi là sạch sẽ, năng động và nhảm nhí.

Cảm ơn.


Người dùng có điểm gì chung không? Thông thường, bạn sẽ nhóm các mục menu thành các nhóm chức năng và gán người dùng cho các nhóm đó (ví dụ: Người dùng quản trị viên, người dùng DB, người giao dịch, v.v.). Sau đó, bạn chỉ cần quản lý các nhóm, tùy thuộc vào lựa chọn công nghệ - điều này có thể được quản lý bằng cách sử dụng một cái gì đó như Active Directory.
Michael

1
Bạn đang nhận được rất nhiều câu trả lời hay ở đây, nhưng những gì bạn có thể muốn đọc là ACL. vi.wikipedia.org/wiki/Access_control_list
Phản ứng

Câu trả lời:


17

Tôi sẽ mô hình hóa nó bằng sơ đồ ER.

  • A PERMISSIONlà quyền truy cập được cấp cho một ROLEtrên MENU_ITEM.
  • ROLE là một tập hợp các quyền được xác định trước được đặt tên
  • A USERcó thể có nhiều ROLE được cấp cho nó.
  • Có quyền được gán cho vai trò thay vì người dùng, giúp việc quản trị quyền dễ dàng hơn nhiều.

nhập mô tả hình ảnh ở đây

Sau đó, bạn có thể tạo chế độ xem để bạn không phải viết các liên kết mỗi lần:

create or replace view v_user_permissions
select
    distinct mi.id, mi.menu_id. mi.label. mi.link, mi.parent, mi.sort, u.user_id
from
    user u
    join user_role ur on (u.user_id = ur.user_id)
    join role ro on (ur.role_id = role.role_id)
    join permission per on (ro.role_id = per.role_id)
    join menu_item mi on (per.metu_item_id = mi.metu_item_id)

Sau đó, mỗi lần bạn muốn biết những mục menu nào người dùng có quyền truy cập, bạn có thể truy vấn nó:

select * from v_user_permissions up where up.user_id = 12736 order by up.sort

BIÊN TẬP:

Vì người dùng có thể có nhiều vai trò được cấp, quyền của vai trò có thể chồng lấp, tức là hai vai trò riêng biệt có thể có quyền truy cập vào cùng một mục menu. Khi bạn xác định một vai trò bạn không biết trước liệu nó có một số quyền chung với các vai trò khác hay không. Nhưng vì đó là về sự kết hợp của các tập hợp, nên vấn đề có được phép hay không là một phần của tập hợp, không phải là nó xuất hiện bao nhiêu lần, do đó là distinctmệnh đề trong khung nhìn.


Thật sự cảm ơn. Không thể hiểu tại sao tôi không thể tự vẽ nó lên. Đoán tôi đã bị chặn và không có kinh nghiệm :)
kéo dài

Ack, tôi nghĩ rằng tôi hiểu nhưng rõ ràng là không. Làm cách nào tôi có thể có nhiều quyền cho một mục menu?
kéo dài

1
@span Bởi vì người dùng có thể có nhiều vai trò được cấp và quyền của vai trò có thể chồng lấp, tức là hai vai trò riêng biệt có thể có quyền truy cập vào cùng một mục menu. Khi bạn xác định vai trò bạn không biết trước nếu vai trò đó sẽ được cấp cùng với vai trò khác có một số quyền chung với vai trò đó. Nhưng vì vấn đề này là về sự kết hợp của các tập hợp, nên vấn đề chỉ là sự cho phép có phải là một phần của tập hợp hay không, không phải là nó xuất hiện bao nhiêu lần.
Tulains Córdova

Cảm ơn, tôi sẽ tiếp tục đọc câu trả lời của bạn cho đến khi tôi nhận được nó;). Tôi nghĩ rằng sai lầm của tôi là khi nghĩ rằng một quyền duy nhất có thể được sử dụng cùng với vai trò để phân tách các mục menu. Có vẻ như tôi cần sự cho phép đối với từng loại 'mục' của mục menu. Một lần nữa, cảm ơn vì sự giúp đỡ của bạn! Tôi sẽ vẽ một số sơ đồ Venn và xem liệu tôi có thể quay đầu xung quanh nó đúng không \ o /
span

5

Có một danh sách được phân tách bằng dấu phẩy có nghĩa là thực hiện so sánh chuỗi con mỗi khi bạn thực hiện truy vấn đối với menu. Điều này là ít hơn lý tưởng.

Bạn cần bình thường hóa bảng:

---------------------------------------------
|Menu items
---------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort|
---------------------------------------------

+---------------------------+
| menu_permission           |
|---------------------------|
| |menu_permission_id| (pk) |
| |menu_id| (fk)            |
| |permission|              |
+---------------------------+

Nếu bạn vẫn muốn danh sách được phân tách bằng dấu phẩy (vì một số lý do), bạn có thể kéo nó ra với những thứ như group_concattrong mysql wm_concat trong orory hoặc các chức năng tương tự trong các ngôn ngữ khác.

Lợi thế cho điều này là nhiều lần.

Đầu tiên, có tính thực tế của cuộc gọi. Thực hiện một chuỗi con đối với một chuỗi lớn tùy ý (nếu bạn sửa kích thước của chuỗi đó, sau này bạn có thể gặp vấn đề với việc điền chuỗi để bạn bắt đầu nhận quyền như athay vì another_permission) có nghĩa là quét chuỗi trên mỗi hàng. Đây không phải là thứ mà cơ sở dữ liệu được tối ưu hóa.

Thứ hai, truy vấn mà bạn viết trở nên đơn giản hơn nhiều. Để xác định xem quyền 'foo' có tồn tại trong danh sách được phân tách bằng dấu phẩy hay không, bạn cần kiểm tra 'foo'.

... permission like "%foo%" ...

Tuy nhiên, điều này sẽ cho kết quả dương tính giả nếu bạn cũng có quyền 'foobar'. Vì vậy, bây giờ bạn cần phải có một bài kiểm tra như

... permission like "%,foo,%" ...

nhưng điều đó sẽ cho kết quả âm tính giả nếu 'foo' ở đầu hoặc cuối chuỗi. Điều đó dẫn đến một cái gì đó như

... (permission like "foo,%" 
  or permission like "%,foo,%" 
  or permission like "%,foo" ) ...

Bạn sẽ lưu ý rằng rất có thể bạn sẽ cần thực hiện nhiều lần quét chuỗi. Cách này dẫn đến sự điên rồ.

Lưu ý với tất cả những điều này, bạn thiếu khả năng thực tế để thực hiện liên kết tham số (vẫn có thể, thậm chí còn xấu hơn).

Bình thường hóa trường cung cấp cho bạn tính linh hoạt và mức độ dễ đọc hơn nhiều trong cơ sở dữ liệu của bạn. Bạn sẽ không hối tiếc.


Cảm ơn bạn đã trả lời tuyệt vời của bạn, nó đã cho tôi thêm kiến ​​thức và tôi rất tuyệt vì điều đó mặc dù tôi nghĩ giải pháp user61852 sẽ phù hợp nhất cho đến bây giờ.
kéo dài

3

Đó là cách tiếp cận cổ điển cho điều này User -> UserGroupvà sau đó được liên kết a Menu -> MenuItem -> UserGroup. Sử dụng một giá trị số nguyên để cân nhắc mức độ cho phép.

-------------------
|Menu
-------------------
|id| |display name|
-------------------

-------------------------------------------------------------
|Menu Item
-------------------------------------------------------------
|id| |menu_id| |label| |link| |parent| |sort| | min_option1
-------------------------------------------------------------

-------------------------------------------
| User
-------------------------------------------
|id| | username | password | user_group_id
-------------------------------------------

-------------------------------------------
| User Group
-------------------------------------------
|id| option1 | option2 | option2
-------------------------------------------

Khi bạn cần hiển thị một menu cho người dùng hiện tại. Bạn có thể truy vấn cơ sở dữ liệu như thế này.

SELECT * FROM `MenuItem`
    LEFT JOIN `UserGroup` ON (`MenuItem`.`user_group_id` = `UserGroup`.`id`)
    LEFT JOIN `User` ON (`UserGroup`.`id` = `User`.`user_group_id` AND `User`.`id` = $current_user_id)
        WHERE `MenuItem`.`menu_id` = $menu_id AND `UserGroup`.`option1` >= `MenuItem`.`min_option1`;

Điều đó sẽ chỉ chọn các menu hiển thị cho người dùng hiện tại dựa trên điều kiện cho option1.

Ngoài ra, nếu bạn lưu trữ chi tiết nhóm người dùng hiện tại trong phiên hiện tại, thì không cần tham gia.

SELECT * FROM `MenuItem` WHERE `MenuItem`.`menu_id` = $menu_id AND `MenuItem`.`min_option1` >= $user_group_option1;

Khi bạn nói về việc muốn lưu trữ nhiều quyền trên mỗi mục menu. Tôi sẽ cẩn thận không nhầm lẫn giữa vai trò người dùng và logic kinh doanh.


2
Thiết kế này sẽ chỉ cho phép mỗi MenuItem được gắn với một Nhóm người dùng duy nhất, nghĩa là các menu bị giới hạn hoặc sao chép dữ liệu. Trong thực tế, bạn muốn có một bảng liên kết, lý tưởng. Ngoài ra, sự lựa chọn của bạn về việc đặt tên bảng DB là số nhiều làm tôi buồn;)
Ed James

@EdWoodcock oh điểm rất tốt. Tôi nên đã đi với một cấp phép (int) và sau đó so sánh với cấp độ nhóm của người dùng. Tôi sẽ thay đổi điều đó. Lưu ý, thói quen tên số nhiều gây ra bởi việc tôi sử dụng CakePHP. Điều này là lạ, vì khung sử dụng các bí danh số ít cho các bảng trong các truy vấn.
Phản ứng

@MatthewFoscarini Đừng lo lắng, tôi không thực sự bận tâm chừng nào một codebase phù hợp;)
Ed James

1
Câu trả lời tuyệt vời. Tôi sẽ ghi nhớ điều này vào lần tới khi tôi làm điều gì đó tương tự. Hiện tại tôi nghĩ giải pháp user61852 sẽ phù hợp nhất vì nó không yêu cầu nhiều thay đổi đối với mã hiện có. Cảm ơn!
kéo dài
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.