Zdaję sobie sprawę, że zabezpieczenia Spring zbudowane są na łańcuchu filtrów, które przechwytują żądanie, wykryją (brak) uwierzytelnienia, przekierowują do punktu wejścia uwierzytelniania lub przekazują żądanie do usługi autoryzacji i ostatecznie pozwalają żądaniu trafić do serwletu lub zgłosić wyjątek bezpieczeństwa (nieuwierzytelnione lub nieautoryzowane). DelegatingFitlerProxy skleja te filtry razem. Aby wykonywać swoje zadania, filtrują usługi dostępu, takie jak UserDetailsService i AuthenticationManager .
Kluczowe filtry w łańcuchu to (w kolejności)
- SecurityContextPersistenceFilter (przywraca uwierzytelnianie z JSESSIONID)
- UsernamePasswordAuthenticationFilter (przeprowadza uwierzytelnianie)
- ExceptionTranslationFilter (przechwytywanie wyjątków bezpieczeństwa z FilterSecurityInterceptor)
- FilterSecurityInterceptor (może generować wyjątki uwierzytelniania i autoryzacji)
Nie wiem, jak są używane te filtry. Czy chodzi o to, że w przypadku logowania formularza dostarczonego przez wiosnę, UsernamePasswordAuthenticationFilter jest używany tylko do / login , a ostatnie filtry nie? Czy element przestrzeni nazw logowania do formularza automatycznie konfiguruje te filtry? Czy każde żądanie (uwierzytelnione lub nie) dociera do FilterSecurityInterceptor w przypadku adresu URL bez logowania?
A jeśli chcę zabezpieczyć REST API tokenem JWT , który jest pobierany z loginu? Muszę skonfigurować dwa http
znaczniki konfiguracji przestrzeni nazw , prawa? Jeden dla / login with UsernamePasswordAuthenticationFilter
, a drugi dla adresów URL REST, z niestandardowym JwtAuthenticationFilter
.
Czy konfiguracja dwóch http
elementów tworzy dwa springSecurityFitlerChains
? UsernamePasswordAuthenticationFilter
Domyślnie jest wyłączone, dopóki nie zadeklaruję form-login
? Jak wymienić SecurityContextPersistenceFilter
na filtr, który otrzyma Authentication
z istniejącego, JWT-token
a nie JSESSIONID
?
źródło
Odpowiedzi:
Łańcuch filtrów zabezpieczających Spring to bardzo złożony i elastyczny silnik.
Patrząc na aktualną dokumentację stabilnej wersji 4.2.1 , w sekcji 13.3 Zamawianie filtrów , można zobaczyć całą organizację filtrów w łańcuchu filtrów:
Teraz spróbuję przejść do kolejnych pytań, jedno po drugim:
Po skonfigurowaniu
<security-http>
sekcji dla każdej z nich musisz podać przynajmniej jeden mechanizm uwierzytelniania. To musi być jeden z filtrów pasujących do grupy 4 w sekcji 13.3 Zamawianie filtrów z dokumentacji Spring Security, do której właśnie się odwołałem.Oto minimalne obowiązujące zabezpieczenia: element http, który można skonfigurować:
<security:http authentication-manager-ref="mainAuthenticationManager" entry-point-ref="serviceAccessDeniedHandler"> <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/> </security:http>
Robiąc to, te filtry są konfigurowane w proxy łańcucha filtrów:
{ "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" }
Uwaga: otrzymuję je, tworząc prosty RestController, który @Autowires the FilterChainProxy i zwraca jego zawartość:
@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; }
Tutaj mogliśmy zobaczyć, że po prostu deklarując
<security:http>
element z jedną minimalną konfiguracją, wszystkie domyślne filtry są uwzględnione, ale żaden z nich nie jest typu uwierzytelniania (czwarta grupa w sekcji 13.3 Kolejność filtrów). Oznacza to więc, że po prostu zadeklarowaniesecurity:http
elementu, SecurityContextPersistenceFilter, ExceptionTranslationFilter i FilterSecurityInterceptor są automatycznie konfigurowane.W rzeczywistości jeden mechanizm przetwarzania uwierzytelniania powinien być skonfigurowany, a nawet ziarna bezpieczeństwa przestrzeni nazw przetwarzają oświadczenia o tym, zgłaszając błąd podczas uruchamiania, ale można to ominąć dodając atrybut punktu wejścia w
<http:security>
Jeśli dodam
<form-login>
do konfiguracji basic to w ten sposób:<security:http authentication-manager-ref="mainAuthenticationManager"> <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/> <security:form-login /> </security:http>
Teraz filterChain będzie wyglądał tak:
{ "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" }
Teraz te dwa filtry org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter i org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter są tworzone i konfigurowane w FilterChainProxy.
A więc teraz pytania:
Tak, jest używany do próby zakończenia mechanizmu przetwarzania logowania w przypadku, gdy żądanie jest zgodne z adresem URL UsernamePasswordAuthenticationFilter. Ten adres URL można skonfigurować lub nawet zmienić jego zachowanie, aby pasowało do każdego żądania.
Możesz także mieć więcej niż jeden mechanizm przetwarzania uwierzytelniania skonfigurowany w tym samym FilterchainProxy (na przykład HttpBasic, CAS itp.).
Nie, element form-login konfiguruje UsernamePasswordAUthenticationFilter, a jeśli nie podasz adresu URL strony logowania, konfiguruje również org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, który kończy się prostym automatycznie wygenerowanym loginem strona.
Pozostałe filtry są domyślnie automatycznie konfigurowane przez utworzenie
<security:http>
elementu bezsecurity:"none"
atrybutu.Każde żądanie powinno do niego dotrzeć, gdyż jest to element, który dba o to, czy żądanie ma prawo dotrzeć do żądanego adresu URL. Ale niektóre z filtrów przetworzonych wcześniej mogą zatrzymać przetwarzanie łańcucha filtrów po prostu nie wywołując
FilterChain.doFilter(request, response);
. Na przykład filtr CSRF może zatrzymać przetwarzanie łańcucha filtrów, jeśli żądanie nie ma parametru csrf.Nie, nie jesteś do tego zmuszony. Możesz zadeklarować oba
UsernamePasswordAuthenticationFilter
i theJwtAuthenticationFilter
w tym samym elemencie http, ale zależy to od konkretnego zachowania każdego z tych filtrów. Oba podejścia są możliwe i ostatecznie wybór zależy od własnych preferencji.Tak to prawda
Tak, możesz to zobaczyć w filtrach podniesionych w każdej z opublikowanych przeze mnie konfiguracji
Możesz uniknąć SecurityContextPersistenceFilter, po prostu skonfiguruj strategię sesji w
<http:element>
. Po prostu skonfiguruj w ten sposób:<security:http create-session="stateless" >
Lub w tym przypadku możesz nadpisać go innym filtrem, w ten sposób wewnątrz
<security:http>
elementu:<security:http ...> <security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/> </security:http> <beans:bean id="myCustomFilter" class="com.xyz.myFilter" />
EDYTOWAĆ:
Ostatecznie zależy to od implementacji samego każdego filtru, ale prawdą jest, że te ostatnie filtry uwierzytelniania są w stanie przynajmniej nadpisać wszelkie wcześniejsze uwierzytelnienia, które ostatecznie zostały wykonane przez poprzednie filtry.
Ale to niekoniecznie się stanie. Mam pewne przypadki produkcyjne w zabezpieczonych usługach REST, w których używam pewnego rodzaju tokenu autoryzacyjnego, który można podać zarówno jako nagłówek HTTP, jak i wewnątrz treści żądania. Więc konfiguruję dwa filtry, które odzyskują ten token, w jednym przypadku z nagłówka HTTP, a drugi z treści żądania własnego żądania odpoczynku. Prawdą jest, że jeśli jedno żądanie HTTP zawiera ten token uwierzytelniania zarówno jako nagłówek HTTP, jak i wewnątrz treści żądania, oba filtry będą próbowały wykonać mechanizm uwierzytelniania delegując go do menedżera, ale można go łatwo uniknąć, po prostu sprawdzając, czy żądanie jest już uwierzytelniony na początku
doFilter()
metody każdego filtra.Posiadanie więcej niż jednego filtru uwierzytelniania wiąże się z posiadaniem więcej niż jednego dostawcy uwierzytelniania, ale nie wymuszaj tego. W przypadku ujawnienia wcześniej mam dwa filtry uwierzytelniania, ale mam tylko jednego dostawcę uwierzytelniania, ponieważ oba filtry tworzą ten sam typ obiektu uwierzytelniania, więc w obu przypadkach menedżer uwierzytelniania deleguje go do tego samego dostawcy.
I przeciwnie, ja też mam scenariusz, w którym publikuję tylko jeden UsernamePasswordAuthenticationFilter, ale poświadczenia użytkownika oba mogą być zawarte w DB lub LDAP, więc mam dwóch dostawców obsługujących UsernamePasswordAuthenticationToken, a AuthenticationManager deleguje każdą próbę uwierzytelnienia z filtru do dostawców w celu potwierdzenia poświadczeń.
Myślę więc, że jest jasne, że ani liczba filtrów uwierzytelniania nie określa liczby dostawców uwierzytelniania, ani liczba dostawców nie determinuje liczby filtrów.
Wcześniej nie przyglądałem się dokładnie temu filtrowi, ale po Twoim ostatnim pytaniu sprawdzałem jego implementację i jak zwykle w Spring prawie wszystko można było skonfigurować, rozszerzyć lub nadpisać.
W SecurityContextPersistenceFilter delegaci w SecurityContextRepository realizacji poszukiwanie SecurityContext. Domyślnie używany jest HttpSessionSecurityContextRepository , ale można to zmienić za pomocą jednego z konstruktorów filtru. Więc może być lepiej napisać SecurityContextRepository, który pasuje do twoich potrzeb i po prostu skonfigurować go w SecurityContextPersistenceFilter, ufając jego sprawdzonemu zachowaniu, zamiast zaczynać wszystko od zera.
źródło
No qualifying bean of type 'org.springframework.security.web.FilterChainProxy' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Spring security to framework oparty na filtrach, który umieszcza WALL (HttpFireWall) przed twoją aplikacją w postaci filtrów proxy lub fasoli zarządzanych przez sprężynę. Twoje żądanie musi przejść przez wiele filtrów, aby dotrzeć do interfejsu API.
Sekwencja wykonania w Spring Security
WebAsyncManagerIntegrationFilter
Zapewnia integrację między SecurityContext i WebAsyncManager Spring Web.SecurityContextPersistenceFilter
Ten filtr będzie wykonywany tylko raz na żądanie, wypełnia SecurityContextHolder informacjami uzyskanymi ze skonfigurowanego SecurityContextRepository przed żądaniem i zapisuje je z powrotem w repozytorium po zakończeniu żądania i wyczyszczeniu kontekstu.Żądanie jest sprawdzane pod kątem istniejącej sesji. Jeśli nowe żądanie, SecurityContext zostanie utworzone, w przeciwnym razie, jeśli żądanie ma sesję, wówczas istniejący kontekst zabezpieczeń zostanie pobrany z repozytorium .
HeaderWriterFilter
Filtruj implementację, aby dodać nagłówki do bieżącej odpowiedzi.LogoutFilter
Jeśli adres URL żądania to/logout
(dla konfiguracji domyślnej) lub jeśli matematyka adresu URL żądaniaRequestMatcher
skonfigurowana wLogoutConfigurer
toLogoutConfigurer
/
skonfigurowanego adresu URL pomyślnego wylogowania lub adresu URL pomyślnego wylogowania albo wywołuje skonfigurowany logoutSuccessHandler.UsernamePasswordAuthenticationFilter
HTTP POST
) domyślny/login
lub pasuje.loginProcessingUrl()
skonfigurowany w,FormLoginConfigurer
wówczasUsernamePasswordAuthenticationFilter
próbuje uwierzytelnić.usernameParameter(String)
,passwordParameter(String)
..loginPage()
zastępuje wartości domyślneAuthentication
przedmiot (UsernamePasswordAuthenticationToken
lub dowolny realizacjaAuthentication
jest tworzony w przypadku niestandardowego filtra auth).authenticationManager.authenticate(authToken)
zostanie wywołanyAuthenticationProvider
metod uwierzytelniania, próbujących wszystkich dostawców uwierzytelniania i sprawdzających dowolnysupports
obiekt authToken / uwierzytelniania dostawcy uwierzytelniania, obsługujący dostawcę uwierzytelniania będzie używany do uwierzytelniania. i zwraca obiekt uwierzytelniania w przypadku pomyślnego uwierzytelnienia, które rzuca elseAuthenticationException
.authenticationSuccessHandler
zostanie wywołana, która przekierowuje do skonfigurowanego docelowego adresu URL (domyślnie/
)SecurityContextHolderAwareRequestFilter
, jeśli używasz go do instalowania HttpServletRequestWrapper obsługującego Spring Security w kontenerze serwletówAnonymousAuthenticationFilter
Wykrywa, czy w SecurityContextHolder nie ma obiektu uwierzytelniania, jeśli nie znaleziono obiektu uwierzytelniania, tworzyAuthentication
obiekt (AnonymousAuthenticationToken
) z przyznanymi uprawnieniamiROLE_ANONYMOUS
. TutajAnonymousAuthenticationToken
ułatwia identyfikację kolejnych żądań nieuwierzytelnionych użytkowników.DEBUG - /app/admin/app-config at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' DEBUG - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@aeef7b36: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
ExceptionTranslationFilter
, aby złapać wszelkie wyjątki Spring Security, aby można było zwrócić odpowiedź błędu HTTP lub uruchomić odpowiedni AuthenticationEntryPointFilterSecurityInterceptor
Pojawi się ten,
FilterSecurityInterceptor
który jest prawie ostatni w łańcuchu filtrów, który pobiera obiekt uwierzytelniania zSecurityContext
i otrzymuje listę przyznanych uprawnień (przyznane role) i podejmie decyzję, czy zezwolić temu żądaniu na dotarcie do żądanego zasobu, czy nie, decyzja jest podejmowana przez dopasowanie z dozwoloneAntMatchers
skonfigurowane wHttpSecurityConfiguration
.Rozważ wyjątki 401-UnAuthorized i 403-Forbidden. Te decyzje zostaną podjęte na końcu łańcucha filtrów
Uwaga: Żądanie użytkownika płynie nie tylko w wyżej wymienionych filtrów, ale istnieją inne filtry też nie pokazane tutaj (.
ConcurrentSessionFilter
,RequestCacheAwareFilter
,SessionManagementFilter
...)To będzie inaczej, kiedy użyć filtru niestandardowego uwierzytelniania zamiast
UsernamePasswordAuthenticationFilter
.Będzie inaczej, jeśli skonfigurujesz filtr uwierzytelniania JWT i
.formLogin() i.e, UsernamePasswordAuthenticationFilter
go pominiesz , będzie to zupełnie inny przypadek.Tylko w celach informacyjnych. Filtry w spring-web i spring-security
Uwaga: odwołaj się do nazwy pakietu na zdjęciu , ponieważ istnieje kilka innych filtrów z orm i mojego niestandardowego zaimplementowanego filtra.
Możesz również wskazać
najpopularniejszy sposób uwierzytelniania nowoczesnej aplikacji internetowej?
różnica między uwierzytelnianiem a autoryzacją w kontekście Spring Security?
źródło
Nie,
UsernamePasswordAuthenticationFilter
rozszerzaAbstractAuthenticationProcessingFilter
, i zawiera aRequestMatcher
, co oznacza, że możesz zdefiniować własny adres URL przetwarzania, ten filtr obsługuje tylkoRequestMatcher
dopasowania adresu URL żądania, domyślny adres URL przetwarzania to/login
.Późniejsze filtry mogą nadal obsługiwać żądanie, jeśli jest
UsernamePasswordAuthenticationFilter
wykonywanachain.doFilter(request, response);
.Więcej informacji o podstawowych fitlerach
UsernamePasswordAuthenticationFilter
jest tworzony przez<form-login>
, są to standardowe aliasy filtrów i kolejnośćZależy to od tego, czy fitlerzy sprzed lat odnoszą sukcesy, ale
FilterSecurityInterceptor
zwykle jest ostatnim fitlerem.Tak, każdy fitlerChain ma
RequestMatcher
, jeśliRequestMatcher
pasuje do żądania, żądanie zostanie obsłużone przez fitlerów w łańcuchu fitler.Wartość domyślna
RequestMatcher
pasuje do wszystkich żądań, jeśli nie skonfigurujesz wzorca lub możesz skonfigurować określony adres URL (<http pattern="/rest/**"
).Jeśli chcesz dowiedzieć się więcej o instalatorach, myślę, że możesz sprawdzić kod źródłowy w wiosennych zabezpieczeniach.
doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
źródło