Rantai filter keamanan Pegas adalah mesin yang sangat kompleks dan fleksibel.
Filter utama dalam rantai adalah (sesuai urutan)
- SecurityContextPersistenceFilter (mengembalikan Otentikasi dari JSESSIONID)
- UsernamePasswordAuthenticationFilter (melakukan otentikasi)
- ExceptionTranslationFilter (menangkap pengecualian keamanan dari FilterSecurityInterceptor)
- FilterSecurityInterceptor (dapat membuang pengecualian otentikasi dan otorisasi)
Melihat dokumentasi stabil 4.2.1 dokumentasi saat ini , bagian 13.3 Memesan Filter Anda dapat melihat organisasi filter seluruh rantai filter:
13.3 Memesan Filter
Urutan bahwa filter didefinisikan dalam rantai sangat penting. Terlepas dari filter mana yang sebenarnya Anda gunakan, urutannya harus sebagai berikut:
ChannelProcessingFilter , karena mungkin perlu mengarahkan ulang ke protokol yang berbeda
SecurityContextPersistenceFilter , sehingga SecurityContext dapat diatur di SecurityContextHolder di awal permintaan web, dan setiap perubahan pada SecurityContext dapat disalin ke HttpSession ketika permintaan web berakhir (siap digunakan dengan permintaan web berikutnya)
ConcurrentSessionFilter , karena menggunakan fungsionalitas SecurityContextHolder dan perlu memperbarui SessionRegistry untuk mencerminkan permintaan yang sedang berlangsung dari kepala sekolah
Mekanisme pemrosesan otentikasi -
UsernamePasswordAuthenticationFilter , CasAuthenticationFilter ,
BasicAuthenticationFilter dll - sehingga SecurityContextHolder dapat dimodifikasi untuk berisi token permintaan otentikasi yang valid
The SecurityContextHolderAwareRequestFilter , jika Anda menggunakannya untuk menginstal menyadari Spring Security HttpServletRequestWrapper ke dalam wadah servlet Anda
The JaasApiIntegrationFilter , jika JaasAuthenticationToken adalah di SecurityContextHolder ini akan memproses filterChain sebagai Subyek dalam JaasAuthenticationToken
RememberMeAuthenticationFilter , sehingga jika tidak ada mekanisme pemrosesan otentikasi sebelumnya memperbarui SecurityContextHolder, dan permintaan menyajikan cookie yang memungkinkan layanan ingat-saya terjadi, objek Otentikasi yang diingat yang cocok akan diletakkan di sana
AnonymousAuthenticationFilter , sehingga jika tidak ada mekanisme pemrosesan otentikasi sebelumnya memperbarui SecurityContextHolder, objek Otentikasi anonim akan diletakkan di sana
ExceptionTranslationFilter , untuk menangkap pengecualian Spring Security sehingga respons kesalahan HTTP dapat dikembalikan atau AuthenticationEntryPoint yang sesuai dapat diluncurkan
FilterSecurityInterceptor , untuk melindungi URI web dan meningkatkan pengecualian ketika akses ditolak
Sekarang, saya akan mencoba melanjutkan pertanyaan Anda satu per satu:
Saya bingung bagaimana filter ini digunakan. Apakah itu untuk form yang disediakan form-login, UsernamePasswordAuthenticationFilter hanya digunakan untuk / login, dan filter yang terakhir tidak? Apakah elemen namespace form-login secara otomatis mengkonfigurasi filter ini? Apakah setiap permintaan (dikonfirmasi atau tidak) mencapai FilterSecurityInterceptor untuk url non-login?
Setelah Anda mengonfigurasi <security-http>
bagian, untuk masing-masing Anda setidaknya harus menyediakan satu mekanisme otentikasi. Ini harus menjadi salah satu filter yang cocok dengan grup 4 di bagian 13.3 Memesan Filter dari dokumentasi Spring Security yang baru saja saya referensikan.
Ini adalah keamanan minimum yang valid: Elemen http yang dapat dikonfigurasi:
<security:http authentication-manager-ref="mainAuthenticationManager"
entry-point-ref="serviceAccessDeniedHandler">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>
Lakukan saja, filter ini dikonfigurasikan dalam proksi rantai filter:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"6": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"7": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"8": "org.springframework.security.web.session.SessionManagementFilter",
"9": "org.springframework.security.web.access.ExceptionTranslationFilter",
"10": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Catatan: Saya mendapatkannya dengan membuat RestController sederhana yang @Mengedit FilterChainProxy dan mengembalikan isinya:
@Autowired
private FilterChainProxy filterChainProxy;
@Override
@RequestMapping("/filterChain")
public @ResponseBody Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
return this.getSecurityFilterChainProxy();
}
public Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
Map<Integer, Map<Integer, String>> filterChains= new HashMap<Integer, Map<Integer, String>>();
int i = 1;
for(SecurityFilterChain secfc : this.filterChainProxy.getFilterChains()){
//filters.put(i++, secfc.getClass().getName());
Map<Integer, String> filters = new HashMap<Integer, String>();
int j = 1;
for(Filter filter : secfc.getFilters()){
filters.put(j++, filter.getClass().getName());
}
filterChains.put(i++, filters);
}
return filterChains;
}
Di sini kita dapat melihat bahwa hanya dengan mendeklarasikan <security:http>
elemen dengan satu konfigurasi minimum, semua filter default disertakan, tetapi tidak ada yang merupakan tipe Otentikasi (grup ke-4 di bagian 13.3 Memesan Filter). Jadi itu sebenarnya berarti bahwa hanya dengan mendeklarasikan security:http
elemen, SecurityContextPersistenceFilter, ExceptionTranslationFilter dan FilterSecurityInterceptor dikonfigurasi secara otomatis.
Bahkan, satu mekanisme pemrosesan otentikasi harus dikonfigurasi, dan bahkan kacang namespace keamanan memproses klaim untuk itu, melemparkan kesalahan selama startup, tetapi itu dapat dilewati dengan menambahkan atribut entry-point-ref di <http:security>
Jika saya menambahkan dasar <form-login>
ke konfigurasi, dengan cara ini:
<security:http authentication-manager-ref="mainAuthenticationManager">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
<security:form-login />
</security:http>
Sekarang, filterChain akan seperti ini:
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter",
"6": "org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter",
"7": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"8": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"9": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"10": "org.springframework.security.web.session.SessionManagementFilter",
"11": "org.springframework.security.web.access.ExceptionTranslationFilter",
"12": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
Sekarang, dua filter ini adalah org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter dan org.springframework.security.web.authentication.ui.KelemahanLoginPageGeneratingFilter dibuat dan dikonfigurasi dalam FilterChainProxy.
Jadi, sekarang, pertanyaannya:
Apakah itu untuk form yang disediakan form-login, UsernamePasswordAuthenticationFilter hanya digunakan untuk / login, dan filter yang terakhir tidak?
Ya, ini digunakan untuk mencoba menyelesaikan mekanisme pemrosesan login seandainya permintaan cocok dengan url UsernamePasswordAuthenticationFilter. URL ini dapat dikonfigurasi atau bahkan diubah perilakunya agar sesuai dengan setiap permintaan.
Anda juga dapat memiliki lebih dari satu mekanisme pemrosesan Otentikasi yang dikonfigurasi dalam FilterchainProxy yang sama (seperti HttpBasic, CAS, dll).
Apakah elemen namespace form-login secara otomatis mengkonfigurasi filter ini?
Tidak, elemen form-login mengonfigurasiFilter UsernamePasswordAUthentication, dan jika Anda tidak memberikan url halaman login, itu juga mengkonfigurasi org.springframework.security.web.authentication.ui.FefaultLoginPageGeneratingFilter, yang berakhir dengan login masuk otomatis yang dibuat secara otomatis. halaman.
Filter lain secara otomatis dikonfigurasi secara default hanya dengan membuat <security:http>
elemen tanpa security:"none"
atribut.
Apakah setiap permintaan (dikonfirmasi atau tidak) mencapai FilterSecurityInterceptor untuk url non-login?
Setiap permintaan harus mencapainya, karena merupakan elemen yang menangani apakah permintaan tersebut memiliki hak untuk mencapai url yang diminta. Tetapi beberapa filter yang diproses sebelumnya mungkin menghentikan pemrosesan rantai filter tanpa menelepon FilterChain.doFilter(request, response);
. Misalnya, filter CSRF mungkin menghentikan pemrosesan rantai filter jika permintaan tidak memiliki parameter csrf.
Bagaimana jika saya ingin mengamankan API REST saya dengan token JWT, yang diambil dari login? Saya harus mengkonfigurasi dua tag http konfigurasi namespace, hak? Satu lagi untuk / masuk dengan UsernamePasswordAuthenticationFilter
, dan satu lagi untuk url REST, dengan kustom JwtAuthenticationFilter
.
Tidak, Anda tidak dipaksa melakukan ini. Anda bisa mendeklarasikan keduanya UsernamePasswordAuthenticationFilter
dan JwtAuthenticationFilter
dalam elemen http yang sama, tetapi itu tergantung pada perilaku konkret dari setiap filter ini. Kedua pendekatan itu mungkin, dan mana yang dipilih secara final tergantung pada preferensi mereka sendiri.
Apakah mengonfigurasi dua elemen http membuat dua springSecurityFitlerChains?
Ya itu benar
Apakah UsernamePasswordAuthenticationFilter dimatikan secara default, sampai saya menyatakan form-login?
Ya, Anda bisa melihatnya di filter yang muncul di setiap konfigurasi yang saya posting
Bagaimana cara mengganti SecurityContextPersistenceFilter dengan satu, yang akan mendapatkan Otentikasi dari token JWT yang sudah ada daripada JSESSIONID?
Anda dapat menghindari SecurityContextPersistenceFilter, cukup mengkonfigurasi strategi sesi di <http:element>
. Cukup konfigurasikan seperti ini:
<security:http create-session="stateless" >
Atau, Dalam hal ini Anda bisa menimpanya dengan filter lain, dengan cara ini di dalam <security:http>
elemen:
<security:http ...>
<security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />
EDIT:
Satu pertanyaan tentang "Anda juga dapat memiliki lebih dari satu mekanisme pemrosesan Otentikasi yang dikonfigurasi dalam FilterchainProxy yang sama". Akankah yang terakhir menimpa otentikasi yang dilakukan oleh yang pertama, jika mendeklarasikan beberapa filter otentikasi (implementasi Spring)? Bagaimana ini terkait dengan memiliki beberapa penyedia otentikasi?
Ini akhirnya tergantung pada implementasi setiap filter itu sendiri, tetapi memang benar fakta bahwa filter otentikasi terakhir setidaknya mampu menimpa otentikasi sebelumnya yang akhirnya dibuat oleh filter sebelumnya.
Tapi ini tidak akan terjadi. Saya memiliki beberapa kasus produksi di layanan REST aman di mana saya menggunakan semacam token otorisasi yang dapat diberikan baik sebagai header Http atau di dalam badan permintaan. Jadi saya mengkonfigurasi dua filter yang memulihkan token itu, dalam satu kasus dari Http Header dan yang lainnya dari badan permintaan dari permintaan sisanya sendiri. Memang benar fakta bahwa jika satu permintaan http memberikan token otentikasi baik sebagai tajuk Http maupun di dalam badan permintaan, kedua filter akan mencoba menjalankan mekanisme otentikasi yang mendelegasikannya kepada manajer, tetapi bisa dengan mudah dihindari hanya dengan memeriksa apakah permintaan tersebut sudah diautentikasi hanya pada awal doFilter()
metode setiap filter.
Memiliki lebih dari satu filter otentikasi terkait dengan memiliki lebih dari satu penyedia otentikasi, tetapi jangan memaksanya. Dalam kasus yang saya paparkan sebelumnya, saya memiliki dua filter otentikasi tetapi saya hanya memiliki satu penyedia otentikasi, karena kedua filter membuat jenis objek otentikasi yang sama sehingga dalam kedua kasus manajer otentikasi mendelegasikannya ke penyedia yang sama.
Dan berlawanan dengan ini, saya juga memiliki skenario di mana saya menerbitkan hanya satu UsernamePasswordAuthenticationFilter tetapi kredensial pengguna keduanya dapat dimuat dalam DB atau LDAP, jadi saya memiliki dua penyedia dukungan UsernamePasswordAuthenticationToken, dan delegasi AuthenticationManager segala upaya otentikasi dari filter ke penyedia. secuensial untuk memvalidasi kredensial.
Jadi, saya pikir sudah jelas bahwa baik jumlah filter otentikasi menentukan jumlah penyedia otentikasi maupun jumlah penyedia menentukan jumlah filter.
Juga, dokumentasi menyatakan SecurityContextPersistenceFilter bertanggung jawab membersihkan SecurityContext, yang penting karena penyatuan utas. Jika saya menghilangkannya atau memberikan implementasi kustom, saya harus menerapkan pembersihan secara manual, kan? Apakah ada yang lebih mirip gotcha ketika menyesuaikan rantai?
Saya tidak melihat dengan saksama filter ini sebelumnya, tetapi setelah pertanyaan terakhir Anda, saya telah memeriksa implementasinya, dan seperti biasanya di Spring, hampir semuanya dapat dikonfigurasikan, diperluas atau ditimpa.
The SecurityContextPersistenceFilter delegasi dalam SecurityContextRepository pelaksanaan pencarian untuk SecurityContext. Secara default, sebuah HttpSessionSecurityContextRepository digunakan, tetapi ini dapat diubah menggunakan salah satu konstruktor filter. Jadi mungkin lebih baik untuk menulis SecurityContextRepository yang sesuai dengan kebutuhan Anda dan hanya mengkonfigurasinya di SecurityContextPersistenceFilter, percaya pada perilaku terbukti itu daripada mulai membuat semuanya dari awal.