asal
Di RABC standar, izin perlu mendukung konfigurasi dinamis, default keamanan musim semi untuk menyetujui izin dalam kode. Skenario bisnis nyata biasanya mengharuskan izin akses peran dikonfigurasi secara dinamis, yaitu, untuk mengonfigurasi peran akses yang sesuai dengan URL saat runtime.
Berdasarkan keamanan musim semi, bagaimana mencapai persyaratan ini?
Cara termudah adalah menyesuaikan filter untuk menyelesaikan penilaian izin, tetapi ini dipisahkan dari kerangka keamanan musim semi. Bagaimana cara mengimplementasikannya secara elegan berdasarkan keamanan musim semi?
Ulasan Otorisasi Keamanan Musim Semi
Spring Security FilterChainProxy digunakan sebagai filter untuk mendaftar dengan web. FilterChainProxy berisi beberapa filter bawaan sekaligus. Pertama, kita perlu memahami berbagai filter yang bawaan dalam keamanan musim semi:
| Alias | Kelas filter | Elemen atau atribut namespace |
|---|---|---|
| Channel_filter | ChannelProcessingFilter | http/intercept-url@membutuhkan-channel |
| Security_context_filter | SecurityContextPersistencefilter | http |
| Concurrent_session_filter | ConcurrentsessionFilter | Sesi-Management/Concurrency-Control |
| Headers_filter | HeaderWriterFilter | http/header |
| Csrf_filter | Csrffilter | http/csrf |
| LOGOUT_FILTER | LOGOUTFILTER | http/logout |
| X509_filter | X509AuthenticationFilter | http/x509 |
| Pre_auth_filter | Subkelas AbstractPreatenticatedProcessingFilter | N/a |
| Cas_filter | CasAuthenticationFilter | N/a |
| Form_login_filter | UsernamepasswordAuthenticationFilter | http/form-login |
| BASIC_AUTH_FILTER | BasicauthenticationFilter | http/http-basic |
| Servlet_api_support_filter | SecurityContExtholderAwarerequestFilter | http/@servlet-api-provision |
| Jaas_api_support_filter | Jaasapiintegrationfilter | http/@jaas-api-provision |
| Remember_me_filter | RememberMeAuthenticationFilter | http/ingat-aku |
| Anonymous_filter | AnonymousAuthenticationFilter | http/anonim |
| Session_management_filter | SessionManagementFilter | manajemen sesi |
| Exception_translation_filter | ExceptionTranslationFilter | http |
| Filter_security_interceptor | FiltersecurityInterceptor | http |
| Switch_user_filter | Switchuserfilter | N/a |
Yang paling penting adalah FiltersecurityInterceptor, yang mengimplementasikan logika otentikasi utama, dan kode inti terbanyak ada di sini:
Protected IntercepTorStatustoken sebelum Invocation (objek objek) {// Dapatkan izin yang diperlukan untuk mengakses koleksi URL <ConfigAttribute> atribut = this.obtainsecurityMetAdataSource () .getAttributes (objek); Otentikasi Authenticated = AuthenticateIfRequired (); // Otentikasi melalui AccessDecisionManager Coba {this.accessDecisionManager.docide (Otentikasi, objek, atribut); } Catch (AccessDenIdException AccessDENIEDException) {publishEvent (Otorisasi baruFailureEvent (objek, atribut, diautentikasi, accessDenIdException)); lempar akses yang diakses; } if (debug) {logger.debug ("otorisasi sukses"); } if (publishAuthorizationSuccessful"); } if (publishAuthorizationSuccess) { publishEvent(new AuthorizedEvent(object, attributes, authenticated)); } // Attempt to run as a different user Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes); if (runAs == null) { if (debug) {Logger.debug ("RunAsManager tidak mengubah objek otentikasi"); SecurthContextholder.getContext ();Seperti yang dapat dilihat dari hal di atas, untuk mencapai otentikasi dinamis, kita dapat mulai dari dua aspek:
Mari kita lihat bagaimana mengimplementasikannya secara terpisah.
ACCESS CUSTOM DECISIONMANAGER
Tiga orang AccessDecisionManagers resmi semuanya didasarkan pada AccessDecisionVoter untuk mengimplementasikan otentikasi izin, jadi kami hanya perlu menyesuaikan AccessDecisionVoter.
Kustomisasi terutama mengimplementasikan antarmuka AccessDecisionVoter. Kami dapat mengimplementasikan rolevoter resmi:
Public Class RolebasedVoter mengimplementasikan AccessDecisionVoter <POMPERTIF> {@Override Public Boolean Supports (Atribut ConfigAttribute) {return true; } @Override Public int vote (otentikasi otentikasi, objek objek, koleksi <ConfigAttribute> atribut) {if (authentication == null) {return access_denied; } int result = access_abstain; Koleksi <? Extends Grantedauthority> Otorities = ExtractAuthority (otentikasi); untuk (configAttribute atribut: atribut) {if (attribute.getAttribute () == null) {lanjutan; } if (this.supports (atribut)) {result = access_denied; // mencoba untuk menemukan otoritas yang diberikan untuk (Grantedauthority Authority: Otorities) {if (attribute.getAttribute (). Equals (Authority.getAuthority ())) {return access_granted; }}}} hasil pengembalian; } Koleksi <? Extends Grantedauthority> ExtractAuthority (otentikasi otentikasi) {return authentication.getauthority (); } @Override Public Boolean Supports (class clazz) {return true; }}Bagaimana cara menambahkan izin dinamis?
Jenis Objek Objek dalam vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) adalah filterInvocation, dan Anda bisa mendapatkan URL dari permintaan saat ini melalui GetRequesturl:
Objek FilterInVocation Fi = (FilterInVocation); String url = fi.getRequesturl ();
Karena itu, ada banyak ruang untuk ekspansi di sini. Anda dapat secara dinamis memuatnya dari DB dan kemudian menilai konfigurasi URL.
Bagaimana cara menggunakan RolebasedVoter ini? Gunakan metode AccessDecisionManager untuk menyesuaikan dalam konfigurasi. Kita harus menggunakan resmi yang tidak dibuka dan kemudian menambahkan voter rolebased yang disesuaikan.
@EnableWebsecurity @enableGlobalMethodSecurity (preposteSlabed = true, sasureNabled = true) Class Configuration Extends WebSecurityConfigurerAdapter {@Override dilindungi configure (httpsecurity http) lemparan Exception {http .addfilterbeCurity htore (http .addfilterbiurity (http.addfilterber (http .addfilterber (http .addfilterberter (http .addfilterblter (http .addfilterberter (http .addfilterpure (http .addfilter (http .addfilter (http.addfilter (http.addfilter (http. UsernamepasswordAuthenticationFilter.class). ExceptionHandling () .AuthenticationEntrypoint (masalah -Upport). AccessdeniedHandler (Masalah mendukung) .and () .csrf () .disable (). Headers () .FrameOptions () .disable () .andan (). .SessionCreationPolicy (sessionCreationPolicy.Stateless) .and () .AuthorizeRequests () // Custom AccessDecisionManager .AccessDecisionManager (AccessDecisionManager ()) .and () .Apply (SecurityConfigurerAdapter ()); } @Bean AccessDecisionManager AccessDecisionManager () {List <accessDecisionVoter <? Extends Object >> DecisionVoters = arrays.aslist (WebExpressionVoter baru (), // rolevoter baru (), baru RolebasedVoter (), new AuthenticateVoter ()); Return New Unanimousbased (DecisionVoters); }Kustomisasi SecurityMetadataSource
Custom FilterInVocationSecurityMetadataSource hanya perlu mengimplementasikan antarmuka, dan secara dinamis memuat aturan dari DB di antarmuka.
Untuk menggunakan kembali definisi dalam kode, kita dapat mengambil SecurityMetadataSource yang dihasilkan dalam kode dan lulus dalam filterInInVocationsEcurityCurityMetAdataSource default di konstruktor.
AppFilterInVocationsecurityMetadataSource kelas publik mengimplementasikan org.springframework.security.web.access.intercept.filterinVocationsEcurityMetadataSource {swasta filterInvocationseCurityMetAdataSource supermetadataSource; @Override Public Collection <ConfigAttribute> getAllConfigAttributes () {return null; } public appFilterInvocationsEcurityMetadataSource (filterInVocationsEcurityMetadataSource ekspresiBasedFilterInVocationsEcurityMetAdataSource) {this.SuperMetAdataSource = ExpressionBasedFilterIvocationsEcurityMetAdataSource; // Konfigurasi Izin Beban TODO dari database} Private Final Antpathmatcher antpathmatcher = baru antpathmatcher (); // Di sini Anda perlu memuat peta akhir pribadi <string, string> urlolemap = new HashMap <string, string> () {{put ("/open/**", "role_anonymous"); put ("/health", "role_anonymous"); put ("/restart", "role_admin"); put ("/demo", "role_user"); }}; @Override Public Collection <ConfigAttribute> getAttributes (Object Object) Melempar IllegalArgumentException {FilterInVocation fi = (FilterInVocation) objek; String url = fi.getRequesturl (); untuk (map.entry <string, string> entri: urlrolemap.entryset ()) {if (antpathmatcher.match (entry.getKey (), url)) {return securityconfig.createList (entri.getValue ()); }} // Kembalikan konfigurasi default yang ditentukan oleh kode return supermetadataSource.getAttributes (objek); } @Override Public Boolean mendukung (kelas <?> Clazz) {return filterinvocation.class.isassignableFrom (clazz); }}Bagaimana cara menggunakannya? Tidak seperti AccessDecisionManager, ExpressionUrlAuthorizationConfigurer tidak memberikan metode yang ditetapkan untuk mengatur filterInVocationsEcurityMetadataSource dari filtersecurity interceptor, bagaimana melakukannya?
Temukan metode ekstensi dengan HjectPostProcessor, yang melaluinya menyesuaikan ObjectPostProcessor yang menangani jenis filtersecurity interceptor, Anda dapat memodifikasi filtersecurityinterceptor.
@EnableWebsecurity @enableGlobalMethodSecurity (preposteSlabed = true, sasureNabled = true) Class Configuration Extends WebSecurityConfigurerAdapter {@Override dilindungi configure (httpsecurity http) lemparan Exception {http .addfilterbeCurity htore (http .addfilterbiurity (http.addfilterber (http .addfilterber (http .addfilterberter (http .addfilterblter (http .addfilterberter (http .addfilterpure (http .addfilter (http .addfilter (http.addfilter (http.addfilter (http. UsernamepasswordAuthenticationFilter.class). ExceptionHandling () .AuthenticationEntrypoint (masalah -Upport). AccessdeniedHandler (Masalah mendukung) .and () .csrf () .disable (). Headers () .FrameOptions () .disable () .andan (). .SessionCreationPolicy (sessionCreationPolicy.Stateless) .and () .AuthorizeRequests () // Custom FilterInVocationsEcurityMetadataSource .WithObjectPostProcessor (ObjectPostProcessor <FiltersecurityInterceptor> () {@override Public <o o ExtendsoryCurityPerIncEncepTor> () {@override Public <o o extendserCurityInterCureCherceptor> FSI.SetSecurityMetadataSource (MySecurityMetadataSource (FSI.GetSecurityMetadataSource ())); } @Bean PUBLIK APPFILTERInVOCationsECURTYMETADataSource mySecurityMetAdataSource (filterInvocationsEcurityMetAdataSource filterInvocationsEcurityMetadataSource) {appFilterInvocationsEcurityMetAdataSource SecurityMetAdataSource = new = AppFilterInvocationsEcurityMetadataSource (FilterInVocationsEcurityMetadataSource); kembalikan SecurityMetadataSource;}ringkasan
Artikel ini memperkenalkan dua metode untuk mengimplementasikan izin dinamis berdasarkan keamanan musim semi. Salah satunya adalah untuk menyesuaikan AccessDecisionManager, dan yang lainnya adalah menyesuaikan filterInVocationsEcurityMetadataSource. Anda dapat memilih secara fleksibel sesuai dengan kebutuhan Anda dalam proyek aktual.
Bacaan lebih lanjut:
Arsitektur keamanan musim semi dan analisis kode sumber
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.