Ada banyak pertanyaan di sini yang berhubungan dengan mekanisme otentikasi dan otorisasi API ISTIRAHAT tetapi tidak satupun dari mereka tampaknya masuk ke detail tentang bagaimana menerapkan layanan aman di tingkat aplikasi.
Sebagai contoh, misalkan webapp saya (saya ingat Java tetapi ini berlaku untuk backend mana pun) memiliki sistem otentikasi aman yang memungkinkan pengguna API untuk login dengan nama pengguna dan kata sandi. Ketika pengguna membuat permintaan, pada titik mana pun selama permintaan memproses pipa saya bisa memanggil getAuthenticatedUser()
metode yang akan mengembalikan pengguna nol jika pengguna tidak masuk, atau objek domain pengguna yang mewakili pengguna yang masuk.
API memungkinkan pengguna terotentikasi untuk mengakses data mereka, mis. GET untuk /api/orders/
akan mengembalikan daftar pesanan pengguna tersebut. Demikian pula, GET untuk /api/tasks/{task_id}
akan mengembalikan data yang berkaitan dengan tugas tertentu.
Mari kita asumsikan bahwa ada sejumlah objek domain berbeda yang dapat dikaitkan dengan akun pengguna (pesanan dan tugas adalah dua contoh, kami juga dapat memiliki pelanggan, faktur, dll.). Kami hanya ingin pengguna yang diautentikasi dapat mengakses data tentang objek mereka sendiri sehingga ketika pengguna melakukan panggilan ke /api/invoices/{invoice_id}
kami perlu memeriksa bahwa pengguna berwenang untuk mengakses sumber daya itu sebelum kami menyajikannya.
Pertanyaan saya adalah, apakah ada pola atau strategi untuk menangani masalah otorisasi ini? Salah satu opsi yang saya pertimbangkan adalah membuat antarmuka pembantu (yaitu SecurityUtils.isUserAuthorized(user, object)
), yang dapat dipanggil selama pemrosesan permintaan untuk memastikan bahwa pengguna berwenang untuk mengambil objek. Ini tidak ideal karena mencemari kode titik akhir aplikasi dengan banyak panggilan ini, mis
Object someEndpoint(int objectId) {
if (!SecurityUtils.isUserAuthorized(loggedInUser, objectDAO.get(objectId)) {
throw new UnauthorizedException();
}
...
}
... dan kemudian ada pertanyaan tentang penerapan metode ini untuk setiap jenis domain yang bisa sedikit menyebalkan. Ini mungkin satu-satunya pilihan tetapi saya tertarik mendengar saran Anda!
Jawaban:
Tolong karena cinta Tuhan jangan buat
SecurityUtils
kelas!Kelas Anda akan menjadi 10rb baris kode spageti dalam hitungan bulan! Anda perlu memiliki
Action
jenis (buat, baca, perbarui, hancurkan, daftar, dll.) Diteruskan keisUserAuthorized()
metode Anda , yang dengan cepat akan menjadiswitch
pernyataan panjang seribu baris dengan logika yang semakin kompleks yang akan sulit untuk unit test. Jangan lakukan itu.Secara umum apa yang saya lakukan, setidaknya di Ruby on Rails, adalah meminta setiap objek domain bertanggung jawab atas hak aksesnya sendiri dengan memiliki kelas kebijakan untuk setiap model . Kemudian, pengontrol menanyakan kelas kebijakan apakah pengguna saat ini untuk permintaan memiliki akses ke sumber daya atau tidak. Berikut ini contoh di Ruby, karena saya belum pernah menerapkan hal seperti itu di Jawa, tetapi gagasan itu harus dijabarkan dengan jelas:
Bahkan jika Anda memiliki sumber daya bersarang yang kompleks, beberapa sumber daya harus 'memiliki' sumber daya bersarang, sehingga logika tingkat atas secara inheren melebur turun. Namun, sumber daya bersarang tersebut membutuhkan kelas kebijakan mereka sendiri jika mereka dapat diperbarui secara terpisah dari sumber daya 'induk'.
Dalam aplikasi saya, yang untuk departemen universitas saya, semuanya berputar di sekitar
Course
objek. Ini bukanUser
aplikasi sentris. Namun,User
sudah terdaftarCourse
, jadi saya bisa memastikan bahwa:@course.users.include? current_user && (whatever_other_logic_I_need)
untuk sumber daya apa pun yang
User
perlu dimodifikasi, karena hampir semua sumber daya terkait dengan aCourse
. Ini dilakukan di kelas kebijakan dalamowns_whatever
metode.Saya belum melakukan banyak arsitektur Java, tetapi tampaknya Anda bisa membuat
Policy
antarmuka, di mana sumber daya yang berbeda yang perlu diautentikasi harus mengimplementasikan antarmuka. Kemudian, Anda memiliki semua metode yang diperlukan yang dapat menjadi serumit yang Anda perlukan untuk per objek domain . Yang penting adalah untuk mengikat logika dengan model itu sendiri, tetapi pada saat yang sama menyimpannya di kelas yang terpisah (prinsip tanggung jawab tunggal (SRP)).Tindakan pengontrol Anda bisa terlihat seperti:
sumber
Solusi yang lebih mudah adalah dengan menggunakan anotasi untuk menandai metode yang memerlukan beberapa bentuk otorisasi. Ini menonjol dari kode bisnis Anda dan dapat ditangani oleh Spring Security atau kode AOP khusus. Jika Anda menggunakan anotasi ini pada metode bisnis Anda daripada titik akhir, Anda dapat memastikan untuk mendapatkan pengecualian ketika pengguna yang tidak sah mencoba memanggil mereka terlepas dari titik masuk.
sumber
@PreAuthorization("hasRole('ADMIN') and #requestingUser.company.uuid == authentication.details.companyUuid")
anotasi di mana#requestingUser
segmen mereferensikan objek yang dipetakan dengan fieldNamerequestingUser
yang memiliki metodegetCompany()
yang mengembalikan objek memiliki metode lebih lanjutgetUuid()
. Theauthentication
mengacu padaAuthentication
objek yang tersimpan dalam konteks keamanan.@PreAuthorize("hasPermission(#user, 'allowDoSomething')")
dan mengimplementasikan evaluator izin khusus Anda atau menulis handler dan root ekspresi kustom . Jika Anda ingin mengubah perilaku anotasi yang tersedia, silakan lihat utas iniGunakan keamanan berbasis kemampuan.
Kemampuan adalah objek yang tidak dapat dilupakan yang bertindak sebagai bukti bahwa seseorang dapat melakukan tindakan tertentu. Pada kasus ini:
Ini membuat tidak mungkin untuk mencoba melakukan sesuatu yang tidak diizinkan dilakukan oleh pengguna saat ini.
Dengan begitu tidak mungkin
sumber