ความแตกต่างระหว่างบทบาทและสิ่งที่ได้รับสิทธิ์ในการรักษาความปลอดภัยของสปริง


227

มีแนวคิดและการใช้งานใน Spring Security เช่นGrantedAuthorityอินเทอร์เฟซเพื่อรับสิทธิ์ในการอนุญาต / ควบคุมการเข้าถึง

ฉันต้องการให้การดำเนินการที่อนุญาตเช่นcreateSubUsersหรือdeleteAccountsซึ่งฉันอนุญาตให้ผู้ดูแลระบบ (มีบทบาทROLE_ADMIN)

ฉันสับสนเนื่องจากบทเรียน / การสาธิตที่ฉันเห็นทางออนไลน์ ฉันพยายามเชื่อมต่อสิ่งที่ฉันอ่าน แต่ฉันคิดว่าเราปฏิบัติต่อทั้งสองอย่างสลับกันได้

ฉันเห็นการhasRoleบริโภคGrantedAuthorityสตริงหรือไม่ ฉันเข้าใจผิดแน่นอนในการทำความเข้าใจ แนวคิดเหล่านี้ใน Spring Security มีอะไรบ้าง

ฉันจะจัดเก็บบทบาทของผู้ใช้แยกจากหน่วยงานผู้มีอำนาจสำหรับบทบาทนั้นได้อย่างไร

ฉันยังดูที่org.springframework.security.core.userdetails.UserDetailsอินเทอร์เฟซที่ใช้ในการรับรองความถูกต้อง - ผู้ให้บริการอ้างอิง DAO ซึ่งใช้User(หมายเหตุล่าสุด GrantedAuthority):

public User(String username, 
            String password, 
            boolean enabled, 
            boolean accountNonExpired,
            boolean credentialsNonExpired, 
            boolean accountNonLocked, 
            Collection<? extends GrantedAuthority> authorities)

หรือมีวิธีอื่นในการแยกความแตกต่างระหว่างสองอีกหรือไม่ หรือมันไม่ได้รับการสนับสนุนและเราต้องทำด้วยตัวเอง?

คำตอบ:


365

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

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

บทบาท (ที่พวกเขาจะนำมาใช้ในหลายตัวอย่าง) เป็นเพียง "สิทธิ์" ที่มีการตั้งชื่อที่บอกว่าบทบาทเป็น GrantedAuthority ROLE_ที่เริ่มต้นด้วยคำนำหน้า ไม่มีอะไรเพิ่มเติม บทบาทเป็นเพียงสิ่งที่ได้รับสิทธิ์ - "สิทธิ์" - "ถูกต้อง" คุณเห็นสถานที่จำนวนมากในการรักษาความปลอดภัยสปริงที่มีการROLE_จัดการบทบาทพร้อมคำนำหน้าเป็นพิเศษเช่นใน RoleVoter ซึ่งROLE_มีการใช้คำนำหน้าเป็นค่าเริ่มต้น สิ่งนี้อนุญาตให้คุณระบุชื่อบทบาทโดยไม่ใช้ROLE_คำนำหน้า ก่อนการรักษาความปลอดภัยของสปริง 4 การจัดการ "บทบาท" นี้ไม่ได้รับการปฏิบัติอย่างสม่ำเสมอและเจ้าหน้าที่และบทบาทมักได้รับการปฏิบัติเช่นเดียวกัน (เช่นคุณเช่นhasAuthority()hasRole()) ด้วย Spring Security 4 การปฏิบัติหน้าที่มีความสอดคล้องกันมากขึ้นและรหัสที่เกี่ยวข้องกับ "บทบาท" (เช่นRoleVoter, การhasRoleแสดงออก ฯลฯ ) จะเพิ่มROLE_คำนำหน้าให้คุณเสมอ ดังนั้นhasAuthority('ROLE_ADMIN')หมายถึงเช่นเดียวกับhasRole('ADMIN')เพราะROLE_คำนำหน้าจะถูกเพิ่มโดยอัตโนมัติ ดูคู่มือการโยกย้ายความปลอดภัยสปริง 3 ถึง 4 สำหรับข้อมูลเพิ่มเติม

แต่ก็ยัง: บทบาทเป็นเพียงผู้มีอำนาจด้วยROLE_คำนำหน้าพิเศษ ดังนั้นในฤดูใบไม้ผลิการรักษาความปลอดภัย 3 @PreAuthorize("hasRole('ROLE_XYZ')")เป็นเช่นเดียวกับ@PreAuthorize("hasAuthority('ROLE_XYZ')")ในฤดูใบไม้ผลิการรักษาความปลอดภัย 4 เป็นเช่นเดียวกับ@PreAuthorize("hasRole('XYZ')")@PreAuthorize("hasAuthority('ROLE_XYZ')")

เกี่ยวกับกรณีการใช้งานของคุณ:

ผู้ใช้มีบทบาทและบทบาทสามารถดำเนินการบางอย่างได้

คุณสามารถจบลงGrantedAuthoritiesด้วยบทบาทที่ผู้ใช้เป็นเจ้าของและการดำเนินการที่บทบาทสามารถทำได้ GrantedAuthoritiesสำหรับบทบาทมีคำนำหน้าและการดำเนินงานมีคำนำหน้าROLE_ OP_ตัวอย่างสำหรับหน่วยงานการดำเนินการอาจจะOP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOBฯลฯ บทบาทสามารถROLE_ADMIN, ROLE_USER,ROLE_OWNERฯลฯ

คุณสามารถท้ายที่สุดให้เอนทิตีของคุณติดตั้งใช้งานได้GrantedAuthorityในตัวอย่างนี้ (รหัสเทียม):

@Entity
class Role implements GrantedAuthority {
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() {
        return id;
    }

    public Collection<GrantedAuthority> getAllowedOperations() {
        return allowedOperations;
    }
}

@Entity
class User {
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() {
        return roles;
    }
}

@Entity
class Operation implements GrantedAuthority {
    @Id
    private String id;

    @Override
    public String getAuthority() {
        return id;
    }
}

รหัสของบทบาทและการดำเนินงานที่คุณสร้างในฐานข้อมูลของคุณจะเป็นตัวแทน GrantedAuthority เช่นROLE_ADMIN,OP_DELETE_ACCOUNTฯลฯ เมื่อผู้ใช้รับรองความถูกต้องให้แน่ใจว่า GrantedAuthorities ทั้งหมดของทุกบทบาทและการดำเนินงานที่สอดคล้องกันจะถูกส่งกลับจาก UserDetails.getAuthorities (ที่) วิธี.

ตัวอย่าง: บทบาทผู้ดูแลระบบที่มี ID ROLE_ADMINมีการดำเนินงานOP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBกำหนดให้มัน บทบาทผู้ใช้ที่มี id ROLE_USERมีการดำเนินการOP_READ_ACCOUNTมีการดำเนินการ

หากมีการบันทึกผู้ดูแลระบบในบริบทความปลอดภัยที่เกิดขึ้นจะมี GrantedAuthorities นี้: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

หากผู้ใช้บันทึกมันจะมี: ROLE_USER ,OP_READ_ACCOUNT

UserDetailsService จะดูแลการรวบรวมบทบาททั้งหมดและการดำเนินการทั้งหมดของบทบาทเหล่านั้นและทำให้พร้อมใช้งานโดยเมธอด getAuthorities () ในอินสแตนซ์ UserDetails ที่ส่งคืน


2
ขอบคุณ! ฉันค้นหาทุกที่เพราะเหตุใด "hasRole ('rolename')" ไม่ทำงานใน Spring 4 -> เอกสารของพวกเขาไม่รวดเร็วอย่างแน่นอน เพียงแค่ "ค้นหาและแทนที่" อย่างรวดเร็วและฉันกลับมาติดตามอีกครั้ง!
JørgenSkår Fischer

12
นี่คือคำตอบที่ดี สิ่งหนึ่งที่ต้องอธิบายให้ชัดเจนแตกต่างกันเล็กน้อยhasRole('xyz')ใน Spring Security 4 คาดว่าคุณจะมีคำนำหน้าROLE_ในขณะที่ผู้hasAuthority('xyz')ไม่คาดหวังคำนำหน้าและประเมินสิ่งที่ผ่านไปฉันใช้โซลูชันนี้ แต่ทำงานเป็นปัญหาhasRole('OP_MY_PERMISSION')ตั้งแต่ROLE_คำนำหน้าเป็นสิ่งที่จำเป็น แต่ฉันควรจะใช้hasAuthority('OP_MY_PERMISSION')เพราะฉันไม่มีคำนำหน้า
randal4

@james คุณสามารถดูคำถามนี้stackoverflow.com/questions/41500031/…
saurabh kumar

1
ใน JSTL springframework.org/security/tags <sec:authorize access="hasRole('ADMIN')">เหมือนกับ<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191

1
ใช่. OP_ เป็นเพียงคำนำหน้าโดยพลการ ROLE_ มีความหมายพิเศษในการติดตั้งสปริงบางตัว
James

9

AFAIK GrantedAuthority และบทบาทเหมือนกันในการรักษาความปลอดภัยฤดูใบไม้ผลิ สตริง getAuthority () ของ GrantedAuthority เป็นบทบาท (ตามการใช้งานเริ่มต้น SimpleGrantedAuthority)

สำหรับกรณีของคุณอาจเป็นคุณสามารถใช้บทบาทตามลำดับชั้น

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_createSubUsers
            ROLE_ADMIN > ROLE_deleteAccounts 
            ROLE_USER > ROLE_viewAccounts
        </value>
    </property>
</bean>

ไม่ใช่โซลที่คุณกำลังมองหา แต่หวังว่ามันจะช่วยได้

แก้ไข : ตอบความคิดเห็นของคุณ

บทบาทเป็นเหมือนสิทธิ์ในการรักษาความปลอดภัยสปริง การใช้ intercept-url กับ hasRole ให้การควบคุมที่ละเอียดมากของการดำเนินการที่อนุญาตสำหรับบทบาท / การอนุญาตใด

วิธีที่เราจัดการในแอปพลิเคชันของเราคือเรากำหนดสิทธิ์ (เช่นบทบาท) สำหรับแต่ละการดำเนินการ (หรือ URL ที่เหลือ) สำหรับเช่น view_account, delete_account, add_account เป็นต้นจากนั้นเราจะสร้างโปรไฟล์เชิงตรรกะสำหรับผู้ใช้แต่ละคนเช่น admin, guest_user, normal_user ส่วนกำหนดค่าเป็นเพียงการจัดกลุ่มแบบลอจิคัลของสิทธิ์โดยไม่ขึ้นกับความปลอดภัยของสปริง เมื่อผู้ใช้ใหม่ถูกเพิ่มโปรไฟล์จะถูกกำหนดให้กับมัน (มีสิทธิ์ที่อนุญาตทั้งหมด) ตอนนี้เมื่อผู้ใช้พยายามดำเนินการบางอย่างการอนุญาต / บทบาทสำหรับการกระทำนั้นจะถูกตรวจสอบกับผู้ใช้ที่ได้รับอนุญาต

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


1
ขอขอบคุณสำหรับความช่วยเหลือของคุณ. ลำดับขั้นดูเหมือนจะเป็นอย่างอื่น วิธีที่ฉันคิดตอนนี้ (ถ้าฉันต้องใช้ Spring Security) คือเก็บทั้งบทบาทและอำนาจในรายการเดียวกันและใช้ hasrole predicate เพื่อทำการตรวจสอบทั้งสองอย่าง กำลังคิดเพิ่มเติมเกี่ยวกับเรื่องนี้ - นี่อาจเป็นความตั้งใจของพวกที่ Spring Security? มีความเป็นไปได้ที่จะใช้ intercept-url นอกเหนือจากการตรวจสอบโดยใช้ hasRole ในรายการทั้งหมดของบทบาทและสิทธิ์ / ผู้มีอำนาจ - หรือแอปพลิเคชันการอนุญาตอื่น ๆ นอกจากนี้จำเป็นต้องนำหน้า ROLE_ อะไรบ้าง มันคือการประชุม?
Chinmay

7

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

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

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

ในทางกลับกัน ROLES เป็นตัวแทนของชุดสิทธิ์ที่หยาบ ROLE_READER จะมีสิทธิ์อ่านหรือดูเท่านั้นในขณะที่ ROLE_EDITOR จะมีทั้งอ่านและเขียน บทบาทส่วนใหญ่จะใช้สำหรับการคัดกรองครั้งแรกที่ชานเมืองของการประมวลผลคำขอเช่น http ... .antMatcher (... ). hasRole (ROLE_MANAGER)

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

เจค


2

เช่นเดียวกับคนอื่น ๆ ที่กล่าวถึงฉันคิดว่าบทบาทเป็นคอนเทนเนอร์สำหรับสิทธิ์ที่ละเอียดมากขึ้น

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

ฉันอาจมีชุดการอนุญาตในแอพบางอย่างเช่น CREATE, READ, UPDATE, DELETE ที่เกี่ยวข้องกับบทบาทของผู้ใช้

หรือสิทธิ์เฉพาะเจาะจงมากขึ้นเช่น READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

สิทธิ์เหล่านี้ค่อนข้างคงที่ แต่ความสัมพันธ์ของบทบาทกับพวกเขาอาจเป็นแบบไดนามิก

ตัวอย่าง -

@Autowired 
RolePermissionsRepository repository;

public void setup(){
  String roleName = "ROLE_ADMIN";
  List<String> permissions = new ArrayList<String>();
  permissions.add("CREATE");
  permissions.add("READ");
  permissions.add("UPDATE");
  permissions.add("DELETE");
  repository.save(new RolePermissions(roleName, permissions));
}

คุณสามารถสร้าง API เพื่อจัดการความสัมพันธ์ของการอนุญาตเหล่านี้กับบทบาท

ฉันไม่ต้องการคัดลอก / วางคำตอบอื่นดังนั้นนี่คือลิงก์ไปยังคำอธิบายที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับ SO
https://stackoverflow.com/a/60251931/1308685

เพื่อใช้งานการนำกลับมาใช้ใหม่ของฉันฉันสร้าง repo โปรดอย่าลังเลที่จะมีส่วนร่วม!
https://github.com/savantly-net/spring-role-permissions

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