Perbedaan antara Peran dan GrantedAuthority dalam Keamanan Musim Semi

227

Ada konsep dan implementasi di Spring Security, seperti GrantedAuthorityantarmuka untuk mendapatkan otoritas untuk mengotorisasi / mengontrol akses.

Saya ingin itu untuk operasi yang diizinkan, seperti createSubUsers , atau deleteAccounts , yang akan saya izinkan ke admin (dengan peran ROLE_ADMIN).

Saya menjadi bingung karena tutorial / demo yang saya lihat online. Saya mencoba menghubungkan apa yang saya baca, tetapi saya pikir kita memperlakukan keduanya secara bergantian.

Saya melihat hasRolemengkonsumsi GrantedAuthoritystring? Saya pasti salah dalam memahami. Apa ini secara konseptual di Spring Security?

Bagaimana cara menyimpan peran pengguna, terpisah dari otoritas untuk peran itu?

Saya juga melihat org.springframework.security.core.userdetails.UserDetailsantarmuka yang digunakan dalam DAO yang direferensikan dengan penyedia otentikasi, yang menggunakan User(perhatikan GrantedAuthority terakhir):

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

Atau adakah cara lain untuk membedakan keduanya? Atau tidak didukung dan kita harus membuatnya sendiri?

Chinmay
sumber

Jawaban:

365

Pikirkan GrantedAuthority sebagai "izin" atau "benar". "Izin" itu (biasanya) dinyatakan sebagai string (dengan getAuthority()metode). String itu memungkinkan Anda mengidentifikasi izin dan membiarkan pemilih Anda memutuskan apakah mereka memberikan akses ke sesuatu.

Anda dapat memberikan GrantedAuthoritys (izin) yang berbeda kepada pengguna dengan menempatkan mereka dalam konteks keamanan. Anda biasanya melakukannya dengan mengimplementasikan UserDetailsService Anda sendiri yang mengembalikan implementasi UserDetails yang mengembalikan GrantedAuthorities yang diperlukan.

Peran (seperti yang digunakan dalam banyak contoh) hanyalah "izin" dengan konvensi penamaan yang mengatakan bahwa peran adalah GrantedAuthority yang dimulai dengan awalan ROLE_. Tidak ada lagi. Peran hanyalah GrantedAuthority - "izin" - "hak". Anda melihat banyak tempat di keamanan pegas di mana peran dengan ROLE_awalannya ditangani secara khusus seperti misalnya di RoleVoter, di mana ROLE_awalan digunakan sebagai default. Ini memungkinkan Anda untuk memberikan nama peran tanpa ROLE_awalan. Sebelum keamanan Musim Semi 4, penanganan khusus "peran" ini tidak diikuti dengan sangat konsisten dan otoritas dan peran sering diperlakukan sama (seperti misalnya Anda misalnyahasAuthority()hasRole()). Dengan Spring Security 4, perlakuan peran lebih konsisten dan kode yang berhubungan dengan "peran" (seperti RoleVoter, hasRoleekspresi dll) selalu menambahkan ROLE_awalan untuk Anda. Jadi hasAuthority('ROLE_ADMIN')artinya sama hasRole('ADMIN')dengan ROLE_awalan yang ditambahkan secara otomatis. Lihat panduan migrasi 3 hingga 4 keamanan pegas untuk informasi lebih lanjut.

Tapi tetap saja: peran hanyalah otoritas dengan ROLE_awalan khusus . Jadi di Spring keamanan 3 @PreAuthorize("hasRole('ROLE_XYZ')")sama dengan @PreAuthorize("hasAuthority('ROLE_XYZ')")dan di Spring keamanan 4 @PreAuthorize("hasRole('XYZ')")sama dengan @PreAuthorize("hasAuthority('ROLE_XYZ')").

Mengenai kasus penggunaan Anda:

Pengguna memiliki peran dan peran dapat melakukan operasi tertentu.

Anda bisa berakhir GrantedAuthoritiesuntuk peran yang dimiliki pengguna dan operasi yang dapat dilakukan peran. Untuk GrantedAuthoritiesperan memiliki awalan ROLE_dan operasi memiliki awalan OP_. Contoh bagi otoritas operasi bisa OP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOBdll Peran bisa ROLE_ADMIN, ROLE_USER, ROLE_OWNERdll

Anda dapat menerapkan entitas Anda GrantedAuthorityseperti dalam contoh (pseudo-code) ini:

@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 peran dan operasi yang Anda buat di database Anda akan menjadi representasi GrantedAuthority, misalnya ROLE_ADMIN, OP_DELETE_ACCOUNTdll. Ketika pengguna diautentikasi, pastikan bahwa semua GrantedAuthorities dari semua perannya dan operasi yang sesuai dikembalikan dari UserDetails.getAuthorities () metode.

Contoh: Peran admin dengan id ROLE_ADMINmemiliki operasi OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBditugaskan untuk itu. Peran pengguna dengan id ROLE_USERmemiliki operasi OP_READ_ACCOUNT.

Jika log admin dalam konteks keamanan yang dihasilkan akan memiliki GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Jika pengguna log, itu akan memiliki: ROLE_USER,OP_READ_ACCOUNT

UserDetailsService akan berhati-hati untuk mengumpulkan semua peran dan semua operasi peran tersebut dan membuatnya tersedia dengan metode getAuthorities () dalam instance UserDetails yang dikembalikan.

James
sumber
2
Terima kasih! Saya telah mencari di mana-mana mengapa "hasRole ('nama file')" tidak berfungsi di Spring 4 -> Dokumentasi mereka tidak persis cepat untuk membaca sekilas. Hanya "cari dan ganti" yang cepat dan saya kembali ke jalur!
Jørgen Skår Fischer
12
Ini jawaban yang bagus. Satu hal yang menjelaskan sedikit berbeda, hasRole('xyz')di Spring Security 4 mengharapkan Anda memiliki awalan ROLE_ sedangkan hasAuthority('xyz')tidak mengharapkan awalan dan mengevaluasi dengan tepat apa yang diteruskan. Saya menggunakan solusi ini, tetapi mengalami masalah dengan hasRole('OP_MY_PERMISSION')sejak yang ROLE_ awalan diperlukan. Sebagai gantinya, saya seharusnya menggunakan hasAuthority('OP_MY_PERMISSION')karena saya tidak memiliki awalan.
randal4
@james dapatkah Anda melihat pertanyaan ini stackoverflow.com/questions/41500031/...
saurabh kumar
1
Dalam JSTL springframework.org/security/tags <sec:authorize access="hasRole('ADMIN')">sama dengan<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191
1
Iya. OP_ hanyalah awalan sembarang. ROLE_ memiliki makna khusus dalam beberapa implementasi pegas.
James
9

AFAIK Granted. Kewenangan dan peran sama dalam keamanan musim semi. String getAuthority () GrantedAuthority adalah peran (sesuai implementasi default SimpleGrantedAuthority).

Untuk kasus Anda mungkin Anda dapat menggunakan Peran Hierarkis

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

Bukan sol yang tepat yang Anda cari, tetapi harap ini membantu

Sunting : Membalas komentar Anda

Peran seperti izin dalam keamanan musim semi. menggunakan intercept-url dengan hasRole memberikan kontrol yang sangat halus tentang operasi apa yang diizinkan untuk peran / izin tersebut.

Cara kita menangani aplikasi kita adalah, kita mendefinisikan izin (yaitu peran) untuk setiap operasi (atau url sisanya) untuk misalnya view_account, delete_account, dll. Kemudian kita membuat profil logis untuk setiap pengguna seperti admin, guest_user, normal_user. Profil ini hanya pengelompokan logis dari izin, tidak tergantung pada keamanan pegas. Ketika pengguna baru ditambahkan, profil ditugaskan untuk itu (memiliki semua izin yang diizinkan). Sekarang ketika pengguna mencoba melakukan beberapa tindakan, izin / peran untuk tindakan tersebut diperiksa terhadap otoritas yang diberikan pengguna.

Juga defaultn RoleVoter menggunakan awalan ROLE_, sehingga otoritas yang memulai dengan ROLE_ dianggap sebagai peran, Anda dapat mengubah perilaku default ini dengan menggunakan kustom RolePrefix di role voter dan menggunakannya dalam keamanan musim semi.

pembuat kode
sumber
1
Terima kasih untuk bantuannya. Hierarki terlihat menjadi sesuatu yang lain. Cara saya berpikir untuk melakukannya sekarang (jika saya harus menggunakan Spring Security) adalah menyimpan peran dan otoritas dalam daftar yang sama dan menggunakan predikat hasRole untuk melakukan pemeriksaan untuk keduanya. Berpikir lebih banyak tentang hal ini - ini mungkin dibiarkan disengaja oleh orang-orang di Spring Security? Apakah ada kemungkinan untuk menggunakan intersep-url selain memeriksa menggunakan hasRole pada daftar lengkap Peran DAN Hak Istimewa / Otoritas - atau aplikasi otorisasi lainnya. Juga, apa perlunya awalan ROLE_? Apakah ini konvensi?
Chinmay
7

Cara lain untuk memahami hubungan antara konsep-konsep ini adalah menafsirkan PERAN sebagai wadah Otoritas.

Otoritas adalah perizinan halus yang menargetkan tindakan tertentu yang terkadang digabungkan dengan lingkup atau konteks data tertentu. Misalnya, Baca, Tulis, Kelola, dapat mewakili berbagai tingkat izin untuk cakupan informasi tertentu.

Selain itu, pihak berwenang diberlakukan jauh di dalam alur pemrosesan permintaan sementara ROLE difilter dengan cara filter permintaan sebelum mencapai Controller. Praktik terbaik menentukan penerapan penegakan otoritas melewati Pengendali di lapisan bisnis.

Di sisi lain, ROLES adalah representasi berbutir kasar dari seperangkat izin. ROLE_READER hanya akan memiliki otoritas Baca atau Lihat sementara ROLE_EDITOR akan memiliki keduanya Baca dan Tulis. Peran terutama digunakan untuk penyaringan pertama di pinggiran pemrosesan permintaan seperti http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

Otoritas yang ditegakkan jauh di dalam alur proses permintaan memungkinkan permohonan izin yang lebih baik. Misalnya, pengguna mungkin memiliki izin Baca Tulis untuk tingkat pertama sumber daya tetapi hanya Baca untuk sub-sumber daya. Memiliki ROLE_READER akan menahan haknya untuk mengedit sumber daya tingkat pertama karena ia memerlukan izin Tulis untuk mengedit sumber ini tetapi pencegat @PreAuthorize dapat memblokir tentatifnya untuk mengedit sub-sumber daya.

Jake

softjake
sumber
2

Seperti yang telah disebutkan orang lain, saya menganggap peran sebagai wadah untuk izin yang lebih rinci.

Meskipun saya menemukan implementasi Peran Hierarki kurang memiliki kontrol yang baik atas izin granular ini.
Jadi saya membuat perpustakaan untuk mengelola hubungan dan menyuntikkan izin sebagai otoritas yang diberikan dalam konteks keamanan.

Saya mungkin memiliki satu set izin di aplikasi, sesuatu seperti BUAT, BACA, PEMBARUAN, HAPUS, yang kemudian dikaitkan dengan Peran pengguna.

Atau izin yang lebih spesifik seperti READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

Izin ini relatif statis, tetapi hubungan peran dengan mereka mungkin dinamis.

Contoh -

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

Anda dapat membuat API untuk mengelola hubungan izin ini dengan suatu peran.

Saya tidak ingin menyalin / menempelkan jawaban lain, jadi inilah tautan ke penjelasan yang lebih lengkap tentang SO.
https://stackoverflow.com/a/60251931/1308685

Untuk menggunakan kembali implementasi saya, saya membuat repo. Silakan berkontribusi!
https://github.com/savantly-net/spring-role-permissions

Jeremy
sumber