Saya memiliki aplikasi web Spring MVC yang menggunakan Spring Security. Saya ingin tahu nama pengguna pengguna yang saat ini masuk. Saya menggunakan cuplikan kode yang diberikan di bawah ini. Apakah ini cara yang diterima?
Saya tidak suka memiliki panggilan ke metode statis di dalam controller ini - yang mengalahkan seluruh tujuan Spring, IMHO. Apakah ada cara untuk mengkonfigurasi aplikasi agar SecurityContext saat ini, atau Otentikasi saat ini, disuntikkan sebagai gantinya?
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
java
spring
spring-mvc
spring-security
Scott Bale
sumber
sumber
Jawaban:
Jika Anda menggunakan Spring 3 , cara termudah adalah:
sumber
Banyak yang telah berubah di dunia Musim Semi sejak pertanyaan ini dijawab. Spring telah menyederhanakan mendapatkan pengguna saat ini di controller. Untuk kacang lainnya, Spring telah mengadopsi saran dari penulis dan menyederhanakan injeksi 'SecurityContextHolder'. Lebih detail ada di komentar.
Ini adalah solusi yang akhirnya saya pakai. Alih-alih menggunakan
SecurityContextHolder
di controller saya, saya ingin menyuntikkan sesuatu yang menggunakan diSecurityContextHolder
bawah tenda tetapi abstrak jauh seperti kelas singleton dari kode saya. Saya tidak menemukan cara untuk melakukan ini selain memutar antarmuka saya sendiri, seperti:Sekarang, controller saya (atau POJO apa pun) akan terlihat seperti ini:
Dan, karena antarmuka menjadi titik decoupling, pengujian unit mudah. Dalam contoh ini saya menggunakan Mockito:
Implementasi default antarmuka terlihat seperti ini:
Dan, akhirnya, konfigurasi Spring produksi terlihat seperti ini:
Tampaknya lebih dari sedikit konyol bahwa Spring, wadah injeksi ketergantungan dari semua hal, belum menyediakan cara untuk menyuntikkan sesuatu yang serupa. Saya mengerti
SecurityContextHolder
diwarisi dari acegi, tapi tetap saja. Masalahnya, mereka sangat dekat - jika hanyaSecurityContextHolder
memiliki pengambil untuk mendapatkanSecurityContextHolderStrategy
contoh yang mendasarinya (yang merupakan antarmuka), Anda bisa menyuntikkan itu. Bahkan, saya bahkan membuka masalah Jira untuk efek itu.Satu hal terakhir - Saya baru saja mengubah jawaban yang saya miliki di sini sebelumnya. Periksa riwayatnya jika Anda penasaran, tetapi, seperti yang ditunjukkan oleh seorang rekan kerja kepada saya, jawaban saya sebelumnya tidak akan berfungsi di lingkungan multi-utas. Yang mendasari yang
SecurityContextHolderStrategy
digunakan olehSecurityContextHolder
adalah, secara default, instance dariThreadLocalSecurityContextHolderStrategy
, yang menyimpanSecurityContext
s dalamThreadLocal
. Oleh karena itu, tidak selalu merupakan ide yang baik untuk menyuntikkanSecurityContext
langsung ke dalam kacang pada waktu inisialisasi - mungkin perlu diambil dariThreadLocal
setiap waktu, dalam lingkungan multi-threaded, sehingga yang benar diambil.sumber
Saya setuju bahwa harus meminta SecurityContext untuk pengguna saat ini bau, tampaknya cara yang sangat un-Spring untuk menangani masalah ini.
Saya menulis kelas "pembantu" statis untuk menangani masalah ini; itu kotor karena itu adalah metode global dan statis, tapi saya pikir cara ini jika kita mengubah apa pun yang terkait dengan Keamanan, setidaknya saya hanya perlu mengubah detail di satu tempat:
sumber
Untuk membuatnya hanya muncul di halaman JSP Anda, Anda dapat menggunakan Spring Security Tag Lib:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
Untuk menggunakan salah satu tag, Anda harus mendeklarasikan taglib keamanan di JSP Anda:
Kemudian di halaman jsp lakukan sesuatu seperti ini:
CATATAN: Seperti yang disebutkan dalam komentar oleh @ SBerg413, Anda harus menambahkan
ke tag "http" di konfigurasi security.xml agar ini berfungsi.
sumber
Jika Anda menggunakan Spring Security ver> = 3.2, Anda dapat menggunakan
@AuthenticationPrincipal
anotasi:Di sini,
CustomUser
adalah objek kustom yang mengimplementasikanUserDetails
yang dikembalikan oleh kebiasaanUserDetailsService
.Informasi lebih lanjut dapat ditemukan di bab @AuthenticationPrincipal dari dokumen referensi Spring Security.
sumber
Saya mendapatkan pengguna terotentikasi oleh HttpServletRequest.getUserPrincipal ();
Contoh:
sumber
null
jika pengguna diautentikasi secara anonim (http
>anonymous
elemen di Spring Security XML).SecurityContextHolder
atauSecurityContextHolderStrategy
cara yang tepat.Di Musim Semi 3+ Anda memiliki opsi berikut.
Pilihan 1 :
Pilihan 2 :
Opsi 3:
Opsi 4: Fancy satu: Lihat ini untuk lebih jelasnya
sumber
@CurrentUser
yang berfungsi seperti kebiasaan@ActiveUser
dari tautan Anda.@AuthenticationPrincipal
dengan@CurrentUser
anotasi khusus . Karena 3.2 kita tidak perlu mengimplementasikan pemecah argumen khusus seperti pada jawaban yang ditautkan. Jawaban lain ini lebih detail.Ya, statika umumnya buruk - umumnya, tetapi dalam kasus ini, statis adalah kode paling aman yang dapat Anda tulis. Karena konteks keamanan mengaitkan Principal dengan utas yang saat ini berjalan, kode yang paling aman akan mengakses statis dari utas secara langsung. Menyembunyikan akses di belakang kelas pembungkus yang disuntikkan memberikan penyerang poin lebih banyak untuk diserang. Mereka tidak memerlukan akses ke kode (yang akan sulit diubah jika toples ditandatangani), mereka hanya perlu cara untuk mengesampingkan konfigurasi, yang dapat dilakukan saat runtime atau memasukkan beberapa XML ke classpath. Bahkan menggunakan injeksi anotasi dalam kode yang ditandatangani akan ditimpa dengan XML eksternal. XML semacam itu dapat menyuntikkan sistem yang sedang berjalan dengan prinsip jahat.
sumber
Saya hanya akan melakukan ini:
sumber
SecurityContextHolderAwareRequestFilter
yang membungkus permintaan dan mengimplementasikan panggilan ini dengan mengaksesSecurityContextHolder
.Untuk aplikasi Spring MVC terakhir yang saya tulis, saya tidak menyuntikkan pemegang SecurityContext, tetapi saya memang memiliki basis controller yang saya punya dua metode utilitas yang terkait dengan ini ... isAuthenticated () & getUsername (). Secara internal mereka melakukan panggilan metode statis yang Anda jelaskan.
Setidaknya itu hanya di satu tempat jika Anda perlu refactor nanti.
sumber
Anda bisa menggunakan pendekatan Spring AOP. Misalnya jika Anda memiliki beberapa layanan, yang perlu diketahui kepala sekolah saat ini. Anda dapat memperkenalkan anotasi khusus yaitu @Principal, yang menunjukkan bahwa Layanan ini harus bergantung pada prinsipal.
Kemudian dalam saran Anda, yang saya pikir perlu memperpanjang MethodBeforeAdvice, periksa bahwa layanan tertentu memiliki penjelasan @Principal dan menyuntikkan nama Prinsipal, atau setel ke 'ANONIM' sebagai gantinya.
sumber
Satu-satunya masalah adalah bahwa bahkan setelah diautentikasi dengan Spring Security, pengguna / pengguna kacang tidak ada dalam wadah, jadi menyuntikkan ketergantungan itu akan sulit. Sebelum kami menggunakan Spring Security, kami akan membuat kacang scoped sesi yang memiliki Kepala Sekolah saat ini, menyuntikkan itu ke "AuthService" dan kemudian menyuntikkan Layanan itu ke sebagian besar layanan lain dalam Aplikasi. Jadi Layanan tersebut hanya akan memanggil authService.getCurrentUser () untuk mendapatkan objek. Jika Anda memiliki tempat dalam kode Anda di mana Anda mendapatkan referensi ke Kepala Sekolah yang sama di sesi, Anda bisa mengaturnya sebagai properti di kacang scoped sesi Anda.
sumber
Coba ini
sumber
Solusi terbaik jika Anda menggunakan Spring 3 dan memerlukan prinsip autentikasi di controller Anda adalah melakukan sesuatu seperti ini:
sumber
Saya menggunakan
@AuthenticationPrincipal
anotasi di@Controller
kelas maupun di@ControllerAdvicer
anotasi. Ex.:Di mana
UserActive
kelas yang saya gunakan untuk layanan pengguna yang dicatat, dan diperluas dariorg.springframework.security.core.userdetails.User
. Sesuatu seperti:Sangat mudah.
sumber
Tetapkan
Principal
sebagai ketergantungan dalam metode pengontrol Anda dan pegas akan menyuntikkan pengguna terotentikasi saat ini dalam metode Anda saat pemanggilan.sumber
Saya suka membagikan cara saya mendukung detail pengguna di halaman freemarker. Semuanya sangat sederhana dan bekerja dengan sempurna!
Anda hanya perlu meletakkan rerequest Authentication di
default-target-url
(halaman setelah form-login) Ini adalah metode Kontroler saya untuk halaman itu:Dan ini adalah kode ftl saya:
Dan itu saja, nama pengguna akan muncul di setiap halaman setelah otorisasi.
sumber