การจัดเก็บรายการเมนูด้วยการอนุญาตของผู้ใช้


11

ฉันกำลังสร้างระบบเมนูใน PHP และ MySQL ฉันจะมีหลายเมนูที่แตกต่างกันและแต่ละเมนูจะมีชุดของรายการเมนูที่เชื่อมต่ออยู่

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

สิ่งที่ฉันมีจนถึงตอนนี้คืออะไร:

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

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

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

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

ฉันชอบที่จะได้ยินความคิดบางอย่างเกี่ยวกับวิธีการจัดโครงสร้างนี้และสิ่งที่อาจถือว่าสะอาดแบบไดนามิกและเส็งเคร็ง

ขอบคุณ


ผู้ใช้มีบางสิ่งที่เหมือนกันหรือไม่ โดยทั่วไปแล้วคุณจะจัดกลุ่มรายการเมนูเป็นกลุ่มการทำงานและกำหนดผู้ใช้ให้กับกลุ่มเหล่านั้น (เช่น - ผู้ดูแลระบบผู้ใช้ DB ผู้ค้า ฯลฯ ) จากนั้นคุณเพียงแค่จัดการการจัดกลุ่มขึ้นอยู่กับทางเลือกของเทคโนโลยี - สิ่งนี้สามารถจัดการได้โดยการใช้งานเช่น Active Directory
Michael

1
คุณได้รับคำตอบที่ดีมากมายที่นี่ แต่สิ่งที่คุณอาจต้องการอ่านคือ ACL en.wikipedia.org/wiki/Access_control_list
ปฏิกิริยาอีกครั้ง

คำตอบ:


18

ฉันจะสร้างแบบจำลองโดยใช้แผนภาพ ER

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

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นคุณสามารถสร้างมุมมองเพื่อให้คุณไม่ต้องเขียนตัวเชื่อมทุกครั้ง:

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)

จากนั้นทุกครั้งที่คุณต้องการทราบว่ารายการเมนูใดที่ผู้ใช้มีสิทธิ์เข้าถึงคุณสามารถสอบถามได้:

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

แก้ไข:

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


เยี่ยมมากขอบคุณ ไม่เห็นว่าทำไมฉันไม่สามารถวาดมันขึ้นมาเองได้ คิดว่าฉันถูกบล็อกและไม่มีประสบการณ์ :)
ช่วง

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

1
@span เนื่องจากผู้ใช้สามารถรับบทบาทได้หลายบทบาทและสิทธิ์ของบทบาทอาจทับซ้อนกันนั่นคือบทบาทที่แตกต่างกันสองรายการสามารถเข้าถึงรายการเมนูเดียวกันได้ เมื่อคุณกำหนดบทบาทที่คุณไม่ทราบล่วงหน้าว่าจะให้บทบาทนั้นกับผู้อื่นที่มีสิทธิ์เหมือนกันหรือไม่ แต่เนื่องจากปัญหานี้เป็นเรื่องเกี่ยวกับการรวมกันของชุดมันเป็นเรื่องสำคัญว่าการอนุญาตที่กำหนดเป็นส่วนหนึ่งของชุดหรือไม่ไม่ใช่กี่ครั้งที่มันปรากฏขึ้น
Tulains Córdova

ขอบคุณฉันจะอ่านคำตอบของคุณต่อไปจนกว่าจะได้รับ;) ฉันคิดว่าข้อผิดพลาดของฉันคือการคิดว่าสามารถใช้สิทธิ์เดียวพร้อมกับบทบาทในการแยกรายการเมนู ดูเหมือนว่าฉันต้องได้รับอนุญาตสำหรับรายการเมนู 'ประเภท' แต่ละรายการ ขอขอบคุณสำหรับความช่วยเหลือของคุณ! ฉันจะวาดไดอะแกรมของ Venn และดูว่าฉันจะเอาหัวไปรอบ ๆ มันได้อย่างถูกต้อง \ o /
ช่วงที่

5

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

คุณต้องทำให้มาตรฐานเป็นปกติ:

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

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

หากคุณยังต้องการรายการที่คั่นด้วยเครื่องหมายจุลภาค (ด้วยเหตุผลบางอย่าง) คุณสามารถดึงออกด้วยสิ่งต่าง ๆ เช่นgroup_concatในmysql wm_concatในoracleหรือฟังก์ชั่นที่คล้ายกันในภาษาอื่น ๆ

ข้อดีสำหรับเรื่องนี้คือหลายเท่า

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

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

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

อย่างไรก็ตามสิ่งนี้จะให้ผลบวกที่ผิดพลาดหากคุณมีสิทธิ์ 'foobar' ด้วย ดังนั้นตอนนี้คุณต้องมีการทดสอบเช่น

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

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

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

คุณจะทราบว่ามีโอกาสมากที่คุณจะต้องทำการสแกนสตริงหลายครั้ง วิธีนี้นำไปสู่ความบ้าคลั่ง

หมายเหตุด้วยสิ่งเหล่านี้คุณขาดความสามารถในการใช้งานจริงในการทำการเชื่อมพารามิเตอร์ (ยังคงเป็นไปได้

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


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

3

ว่าวิธีการคลาสสิกนี้ถูกแล้วที่เกี่ยวข้องUser -> UserGroup Menu -> MenuItem -> UserGroupการใช้ค่าจำนวนเต็มเพื่อชั่งน้ำหนักระดับสิทธิ์

-------------------
|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
-------------------------------------------

เมื่อคุณต้องการแสดงเมนูสำหรับผู้ใช้ปัจจุบัน คุณสามารถสืบค้นฐานข้อมูลเช่นนี้

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`;

option1ว่าจะเลือกเมนูที่มองเห็นให้กับผู้ใช้ในปัจจุบันขึ้นอยู่กับเงื่อนไขสำหรับ

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

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

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


2
การออกแบบนี้จะอนุญาตให้แต่ละ MenuItem เชื่อมโยงกับ UserGroup เดียวซึ่งหมายถึงเมนูที่ จำกัด หรือการทำสำเนาข้อมูล ในความเป็นจริงคุณต้องการตารางลิงค์นึกคิด นอกจากนี้ยังมีทางเลือกของการตั้งชื่อตาราง DB เป็นพหูพจน์ทำให้ฉันเศร้า;)
เอ็ดเจมส์

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

@MatthewFoscarini ไม่ต้องกังวลผมไม่ได้ใส่ใจจริงๆตราบเท่าที่ codebase สอดคล้อง;)
เอ็ดเจมส์

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