Sự khác biệt giữa Vai trò và Quyền được bảo đảm trong Bảo mật Mùa xuân


227

Có các khái niệm và triển khai trong Spring Security, chẳng hạn như GrantedAuthoritygiao diện để có thẩm quyền cho phép / kiểm soát quyền truy cập.

Tôi muốn rằng để hoạt động cho phép, chẳng hạn như createSubUsers , hoặc deleteAccounts , mà tôi sẽ cho phép một quản trị (với vai trò ROLE_ADMIN).

Tôi đang bị lẫn lộn khi các hướng dẫn / bản demo tôi thấy trên mạng. Tôi cố gắng kết nối những gì tôi đọc, nhưng tôi nghĩ chúng ta đối xử với nhau.

Tôi thấy hasRoletiêu thụ một GrantedAuthoritychuỗi? Tôi chắc chắn đang làm điều đó sai trong sự hiểu biết. Những khái niệm này trong Spring Security là gì?

Làm cách nào để lưu trữ vai trò của người dùng, tách biệt với chính quyền cho vai trò đó?

Tôi cũng đang xem org.springframework.security.core.userdetails.UserDetailsgiao diện được sử dụng trong DAO được nhà cung cấp xác thực tham chiếu, tiêu thụ một User(lưu ý GrantedAuthority cuối cùng):

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

Hoặc có cách nào khác để phân biệt hai cái kia không? Hoặc nó không được hỗ trợ và chúng ta phải làm cho riêng mình?

Câu trả lời:


365

Hãy nghĩ về một GrantedAuthority là "sự cho phép" hoặc "quyền". Các "quyền" đó (thông thường) được biểu thị dưới dạng chuỗi (với getAuthority()phương thức). Các chuỗi đó cho phép bạn xác định các quyền và để cử tri của bạn quyết định nếu họ cấp quyền truy cập vào một cái gì đó.

Bạn có thể cấp GrantedAuthority (quyền) khác nhau cho người dùng bằng cách đưa họ vào bối cảnh bảo mật. Bạn thường làm điều đó bằng cách triển khai UserDetailsService của riêng bạn, trả về triển khai UserDetails trả về GrantedAuthorities cần thiết.

Các vai trò (vì chúng được sử dụng trong nhiều ví dụ) chỉ là "quyền" với quy ước đặt tên cho biết vai trò là GrantedAuthority bắt đầu bằng tiền tố ROLE_. Không còn gì nữa. Một vai trò chỉ là GrantedAuthority - "quyền" - "quyền". Bạn thấy rất nhiều vị trí trong bảo mật mùa xuân trong đó vai trò với ROLE_tiền tố của nó được xử lý đặc biệt, ví dụ như trong RoleVoter, trong đó ROLE_tiền tố được sử dụng làm mặc định. Điều này cho phép bạn cung cấp các tên vai trò mà không cần ROLE_tiền tố. Trước Bảo mật mùa xuân 4, việc xử lý "vai trò" đặc biệt này đã không được tuân thủ một cách nhất quán và chính quyền và vai trò thường được đối xử như nhau (ví dụ như bạnhasAuthority()hasRole()). Với Xuân An 4, việc điều trị các vai trò là phù hợp và mã hơn là giao dịch với "vai trò" (như RoleVoter, các hasRolebiểu hiện, vv) luôn thêm ROLE_tiền tố cho bạn. Vì vậy, hasAuthority('ROLE_ADMIN')có nghĩa tương tự như hasRole('ADMIN')ROLE_tiền tố được thêm tự động. Xem hướng dẫn di chuyển 3 đến 4 bảo mật mùa xuân để biết thêm thông tin.

Nhưng vẫn: một vai trò chỉ là một cơ quan có ROLE_tiền tố đặc biệt . Vì vậy, trong bảo mật mùa xuân 3 @PreAuthorize("hasRole('ROLE_XYZ')")cũng giống như @PreAuthorize("hasAuthority('ROLE_XYZ')")và trong bảo mật mùa xuân 4 @PreAuthorize("hasRole('XYZ')")cũng giống như vậy @PreAuthorize("hasAuthority('ROLE_XYZ')").

Về trường hợp sử dụng của bạn:

Người dùng có vai trò và vai trò có thể thực hiện các hoạt động nhất định.

Bạn có thể kết thúc GrantedAuthoritiesvới vai trò của người dùng và các hoạt động mà vai trò có thể thực hiện. Các GrantedAuthoritiesvai trò có tiền tố ROLE_và các hoạt động có tiền tố OP_. Một ví dụ cho các cơ quan hoạt động có thể là OP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOB, vv Vai trò có thể ROLE_ADMIN, ROLE_USER, ROLE_OWNER, vv

Cuối cùng, bạn có thể thực hiện các thực thể của mình GrantedAuthoritynhư trong ví dụ (mã giả) này:

@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;
    }
}

Id của các vai trò và hoạt động bạn tạo trong cơ sở dữ liệu của bạn sẽ là đại diện GrantedAuthority ROLE_ADMIN, OP_DELETE_ACCOUNTv.v. Khi người dùng được xác thực, hãy đảm bảo rằng tất cả các GrantedAuthorities của tất cả các vai trò của nó và các hoạt động tương ứng đều được trả về từ UserDetails.getAuthorities () phương pháp.

Ví dụ: Vai trò quản trị với id ROLE_ADMINcó các hoạt động OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBgán cho nó. Vai trò người dùng với id ROLE_USERcó hoạt động OP_READ_ACCOUNT.

Nếu một admin logs trong kết quả bối cảnh an ninh sẽ có GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Nếu người dùng đăng nhập nó, nó sẽ có : ROLE_USER,OP_READ_ACCOUNT

UserDetailsService sẽ chăm sóc để thu thập tất cả các vai trò và tất cả các hoạt động của các vai trò đó và làm cho chúng có sẵn bằng phương thức getAuthorities () trong ví dụ UserDetails được trả về.


2
Cảm ơn bạn! Tôi đã tìm kiếm ở khắp mọi nơi tại sao "hasRole ('rolename')" không hoạt động trong Mùa xuân 4 -> Tài liệu của họ không chính xác nhanh chóng lướt qua. Chỉ cần "tìm và thay thế" nhanh chóng và tôi đã trở lại đúng hướng!
Jørgen Skår Fischer

12
Đây là một câu trả lời tuyệt vời. Một điều cần làm rõ để giải thích một chút khác nhau, các hasRole('xyz')mùa xuân Security 4 dự đoán bạn có những ROLE_ tiền tố trong khi hasAuthority('xyz')không mong đợi các tiền tố và đánh giá lại chính xác những gì được thông qua vào. Tôi sử dụng giải pháp này, nhưng đã chạy vào vấn đề với hasRole('OP_MY_PERMISSION')từ các ROLE_ tiền tố là cần thiết. Thay vào đó, tôi nên sử dụng hasAuthority('OP_MY_PERMISSION')vì tôi không có tiền tố.
randal4

@james bạn có thể xem câu hỏi này stackoverflow.com/questions/41500031/ từ
saurabh kumar

1
Trong JSTL springframework.org/security/tags <sec:authorize access="hasRole('ADMIN')">giống như<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191

1
Đúng. OP_ chỉ là một tiền tố tùy ý. ROLE_ có ý nghĩa đặc biệt trong một số triển khai mùa xuân.
James

9

AFAIK GrantedAuthority và vai trò giống nhau trong bảo mật mùa xuân. Chuỗi getAuthority () của GrantedAuthority là vai trò (theo cách triển khai mặc định SimpleGrantedAuthority).

Đối với trường hợp của bạn, bạn có thể sử dụng Vai trò phân cấp

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

Không phải là sol chính xác mà bạn đang tìm kiếm, nhưng hy vọng nó sẽ giúp

Chỉnh sửa : Trả lời bình luận của bạn

Vai trò giống như một sự cho phép trong bảo mật mùa xuân. sử dụng url chặn với hasRole cung cấp một kiểm soát chi tiết rất tốt về hoạt động nào được phép cho vai trò / quyền nào.

Cách chúng tôi xử lý trong ứng dụng của mình là, chúng tôi xác định quyền (nghĩa là vai trò) cho từng thao tác (hoặc url nghỉ ngơi), ví dụ: view_account, xóa_account, add_account, v.v. Sau đó, chúng tôi tạo hồ sơ logic cho từng người dùng như admin, guest_user, normal_user. Các hồ sơ chỉ là nhóm hợp lý của các quyền, độc lập với bảo mật mùa xuân. Khi một người dùng mới được thêm vào, một hồ sơ được gán cho nó (có tất cả các quyền cho phép). Bây giờ, khi người dùng cố gắng thực hiện một số hành động, quyền / vai trò cho hành động đó được kiểm tra đối với người dùng được cấp.

Ngoài ra, RoleVoter mặc định sử dụng tiền tố ROLE_, do đó, bất kỳ quyền hạn nào bắt đầu với ROLE_ đều được coi là vai trò, bạn có thể thay đổi hành vi mặc định này bằng cách sử dụng một vai trò tùy chỉnh trong vai trò cử tri và sử dụng nó trong bảo mật mùa xuân.


1
Cảm ơn sự giúp đỡ của bạn. Thứ bậc có vẻ là một cái gì đó khác. Cách tôi nghĩ bây giờ (nếu tôi phải sử dụng Spring Security) sẽ lưu trữ cả vai trò và quyền hạn trong cùng một danh sách và sử dụng vị từ hasRole để thực hiện kiểm tra cho cả hai. Suy nghĩ nhiều hơn về nó - điều này có lẽ là do những kẻ cố ý tại Spring Security? Có khả năng sử dụng url chặn ngoài việc kiểm tra bằng hasRole trong danh sách đầy đủ các Vai trò và Đặc quyền / Quyền hạn - hoặc các ứng dụng ủy quyền khác. Ngoài ra, cần phải có tiền tố ROLE_ là gì? Có phải là quy ước?
Chinmay

7

Một cách khác để hiểu mối quan hệ giữa các khái niệm này là diễn giải ROLE như một kho chứa Chính quyền.

Các nhà chức trách là các quyền chi tiết tốt nhắm mục tiêu một hành động cụ thể đôi khi được kết hợp với phạm vi dữ liệu hoặc bối cảnh cụ thể. Chẳng hạn, Đọc, Viết, Quản lý, có thể biểu thị các cấp quyền khác nhau cho một phạm vi thông tin nhất định.

Ngoài ra, các nhà chức trách được thực thi sâu trong luồng xử lý của một yêu cầu trong khi ROLE được lọc theo cách lọc yêu cầu trước khi đến Bộ điều khiển. Thực tiễn tốt nhất quy định việc thực thi các cơ quan thực thi qua Bộ điều khiển trong lớp kinh doanh.

Mặt khác, ROLES là đại diện chi tiết thô của một tập hợp các quyền. ROLE_READER sẽ chỉ có quyền Đọc hoặc Xem trong khi ROLE_EDITOR sẽ có cả Đọc và Viết. Vai trò chủ yếu được sử dụng cho sàng lọc đầu tiên ở phần ngoài của quá trình xử lý yêu cầu, chẳng hạn như http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

Các cơ quan có thẩm quyền được thi hành sâu trong quy trình xử lý của yêu cầu cho phép ứng dụng cấp phép chi tiết hơn. Chẳng hạn, người dùng có thể có quyền Đọc ghi đối với cấp tài nguyên đầu tiên nhưng chỉ Đọc đối với tài nguyên phụ. Có ROLE_READER sẽ hạn chế quyền chỉnh sửa tài nguyên cấp đầu tiên vì anh ta cần có quyền Viết để chỉnh sửa tài nguyên này nhưng một kẻ chặn @PreAuthorize có thể chặn dự kiến ​​của anh ta để chỉnh sửa tài nguyên phụ.

Jake


2

Giống như những người khác đã đề cập, tôi nghĩ về vai trò như các thùng chứa cho các quyền chi tiết hơn.

Mặc dù tôi thấy việc thực hiện Vai trò phân cấp là thiếu kiểm soát tốt đối với các quyền này.
Vì vậy, tôi đã tạo một thư viện để quản lý các mối quan hệ và tiêm quyền như các cơ quan có thẩm quyền trong bối cảnh bảo mật.

Tôi có thể có một bộ quyền trong ứng dụng, một cái gì đó như TẠO, ĐỌC, CẬP NHẬT, XÓA, sau đó được liên kết với Vai trò của người dùng.

Hoặc các quyền cụ thể hơn như READ_POST, READ_PUBOUNDED_POST, CREATE_POST, PUBlish_POST

Các quyền này tương đối tĩnh, nhưng mối quan hệ của các vai trò với chúng có thể là động.

Thí dụ -

@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));
}

Bạn có thể tạo API để quản lý mối quan hệ của các quyền này với vai trò.

Tôi không muốn sao chép / dán câu trả lời khác, vì vậy đây là liên kết đến một lời giải thích đầy đủ hơn về SO.
https://stackoverflow.com/a/60251931/1308685

Để sử dụng lại việc thực hiện của tôi, tôi đã tạo một repo. Xin vui lòng đóng góp!
https://github.com/savantly-net/spring-role-permissions

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.