Jak działa Łańcuch filtrów sprężynowych

Zdaję sobie sprawę, że Spring security opiera się na łańcuchu filtrów, które przechwycą żądanie, wykryją (brak) uwierzytelnienia, przekierują do punktu wejścia uwierzytelnienia lub przekażą żądanie do usługi autoryzacyjnej i ostatecznie pozwolą żądaniu albo trafić w servlet, albo wyrzucić wyjątek bezpieczeństwa (nieautoryzowany lub nieautoryzowany). skleja te filtry razem. Aby wykonać swoje zadania, te usługi dostępu do filtrów, takie jak UserDetailsService oraz AuthenticationManager .

Kluczowe filtry w łańcuchu to (w kolejności)

    SecurityContextPersistenceFilter (przywraca uwierzytelnianie z JSESSIONID) W tym celu należy wykonać następujące czynności:]}
  • ExceptionTranslationFilter (wyłapywanie WYJĄTKÓW bezpieczeństwa z FilterSecurityInterceptor)
  • FilterSecurityInterceptor (może wyrzucać wyjątki od uwierzytelniania i autoryzacji)

I ' m confused how te filtry są używane. Czy jest tak, że dla formularza-login, UsernamePasswordAuthenticationFilter jest używany tylko dla / login , a ostatnie filtry nie są? Czy element przestrzeni nazw form-login automatycznie konfiguruje te filtry? Czy każde żądanie (uwierzytelnione lub nie) dociera do FilterSecurityInterceptor dla adresu URL bez logowania?

Co zrobić, jeśli chcę zabezpieczyć moje REST API za pomocą JWT-token , który jest pobierany z logowania? muszę konfiguracja dwóch przestrzeni nazw konfiguracja http tagi, prawa? Inne dla / login z UsernamePasswordAuthenticationFilter, a inne dla REST url ' s, z niestandardowym JwtAuthenticationFilter.

Czy konfigurowanie dwóch http elementów tworzy dwa springSecurityFitlerChains? Czy UsernamePasswordAuthenticationFilter jest domyślnie wyłączone, dopóki nie zadeklaruję form-login? Jak zamienić SecurityContextPersistenceFilter na jeden, który uzyska Authentication z istniejącego JWT-token zamiast JSESSIONID?

Author: Tuomas Toivonen, 2017-01-05

2 answers

[22]}sprężynowy łańcuch filtra bezpieczeństwa jest bardzo złożonym i elastycznym silnikiem.

Kluczowe filtry w łańcuchu to (w kolejności)

    SecurityContextPersistenceFilter (przywraca uwierzytelnianie z JSESSIONID) W tym celu należy wykonać następujące czynności:]}
  • ExceptionTranslationFilter (wyłapywanie WYJĄTKÓW bezpieczeństwa z FilterSecurityInterceptor)
  • FilterSecurityInterceptor (może rzucać uwierzytelnianie i wyjątki autoryzacji)

Patrząc na aktualną dokumentację stabilnego wydania 4.2.1, sekcja 13.3 porządkowanie filtrów można było zobaczyć organizację filtrów całego łańcucha filtrów:

13.3 Porządkowanie Filtrów

Kolejność filtrów zdefiniowana w łańcuchu jest bardzo ważna. Niezależnie od tego, jakich filtrów faktycznie używasz, zamówienie powinno być następujące:

  1. ChannelProcessingFilter, ponieważ może być konieczne przekierowanie do innego protokołu

  2. SecurityContextPersistenceFilter, Tak więc SecurityContext można skonfigurować w narzędziu SecurityContextHolder na początku żądania internetowego i wszelkie zmiany SecurityContext można skopiować do HttpSession po zakończeniu żądania internetowego (gotowy do użycia z następnym żądaniem internetowym)

  3. ConcurrentSessionFilter, ponieważ korzysta z funkcji SecurityContextHolder i musi zaktualizować SessionRegistry, aby odzwierciedlić bieżące żądania od głównego

  4. Mechanizmy przetwarzania uwierzytelniania - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter , BasicAuthenticationFilter etc-tak, aby SecurityContextHolder mógł być modyfikowane do zawiera poprawny Token żądania uwierzytelnienia

  5. Na SecurityContextHolderAwareRequestfilter, jeśli używasz go do zainstalowania Spring Security aware HttpServletRequestWrapper w swoim servlet container

  6. Na JaasApiIntegrationFilter, jeśli JaasAuthenticationToken znajduje się w narzędziu SecurityContextHolder, proces ten przetworzy Łańcuch filtrów jako Temat w JaasAuthenticationToken

  7. RememberMeAuthenticationFilter, tak, że w przypadku braku wcześniejszego mechanizmu przetwarzania uwierzytelniania zaktualizowano SecurityContextHolder, a żądanie przedstawia plik cookie, który umożliwia usługom remember-me się, zostanie umieszczony odpowiedni obiekt uwierzytelniający tam

  8. AnonymousAuthenticationFilter, tak, że w przypadku braku wcześniejszego mechanizmu przetwarzania uwierzytelniania zaktualizowano SecurityContextHolder, na obiekt uwierzytelniania anonimowego zostanie tam umieszczony

  9. ExceptionTranslationFilter, aby wyłapać wszelkie wyjątki związane z zabezpieczeniami Spring, aby można było zwrócić odpowiedź na Błąd HTTP lub można uruchomić odpowiednie uwierzytelnienie

  10. FilterSecurityInterceptor, aby chronić Uri internetowe i zgłaszać wyjątki w przypadku odmowy dostępu

Teraz, postaram się przejść przez twój pytania po kolei:

Jestem zdezorientowany, jak te filtry są używane. Czy to na wiosnę podany formularz-login, UsernamePasswordAuthenticationFilter jest używany tylko dla / login, a ostatnie filtry nie? Czy przestrzeń nazw form-login element automatycznie skonfigurować te filtry? Czy każda prośba (uwierzytelnione lub nie) Reach FilterSecurityInterceptor dla braku logowania url?

Po skonfigurowaniu <security-http> sekcji, dla każdej z nich musisz przynajmniej podać jedną mechanizm uwierzytelniania. To musi być jeden z filtrów, które pasują do grupy 4 w sekcji 13.3 kolejność filtrów z dokumentacji Spring Security, o której właśnie wspomniałem.

Jest to minimalny ważny element security: 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>
Po prostu robiąc to, Filtry te są skonfigurowane 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: dostaję je tworząc prosty RestController, który @ Autowires filterchainproxy i zwraca to spis treści:

    @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 widzimy, że po prostu deklarując element <security:http> z jedną minimalną konfiguracją, wszystkie domyślne filtry są włączone, ale żaden z nich nie jest typu uwierzytelniania (czwarta grupa w sekcji 13.3 kolejność filtrów). Oznacza to, że po prostu deklarując element security:http, SecurityContextPersistenceFilter, Exceptiontranslationfilter i FilterSecurityInterceptor są automatycznie skonfigurowane.

W rzeczywistości jeden mechanizm przetwarzania uwierzytelniania należy skonfigurować, a nawet zabezpieczyć przestrzeń nazw Beans przetwarzając twierdzenia o tym, rzucając błąd podczas uruchamiania, ale można go ominąć dodając atrybut entry-point-ref w <http:security>

Jeśli dodam podstawowe <form-login> do konfiguracji, 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 łańcuch filtrów 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.Ochrona.www.uwierzytelnianie.UsernamePasswordAuthenticationFilter oraz org.springframework.Ochrona.www.uwierzytelnianie.ui.DefaultLoginPageGeneratingFilter są tworzone i konfigurowane w FilterChainProxy.

Więc teraz pytania:

Czy chodzi o to, że na wiosnę podany formularz-login, UsernamePasswordAuthenticationFilter jest używany tylko dla / login, oraz Ostatnie filtry nie są?

Tak, jest używany do próby zakończenia mechanizmu przetwarzania logowania w przypadku, gdy żądanie pasuje do adresu URL UsernamePasswordAuthenticationFilter. To adres url można skonfigurować, a nawet zmienić jego zachowanie, aby pasował do każdego żądania.

Możesz mieć więcej niż jeden mechanizm przetwarzania uwierzytelniania skonfigurowany w tym samym FilterchainProxy (taki jak HttpBasic, CAS, itp.).

Czy element przestrzeni nazw form-login automatycznie konfiguruje te filtry?

Nie, element form-login konfiguruje UsernamePasswordAUthenticationFilter, a jeśli nie podasz adresu URL strony logowania, konfiguruje również org.springframework.Ochrona.www.uwierzytelnianie.ui.DefaultLoginPageGeneratingFilter, który kończy się prostą automatycznie wygenerowaną stroną logowania.

Pozostałe filtry są domyślnie automatycznie konfigurowane przez utworzenie elementu <security:http> bez atrybutu security:"none".

Czy każde żądanie (uwierzytelnione lub nie) dociera do FilterSecurityInterceptor dla adresu URL bez logowania?

Każde żądanie powinno do niego dotrzeć, ponieważ jest to element, który dba o to, czy żądanie ma prawa aby dotrzeć do żądanego adresu url. Ale niektóre z filtrów przetwarzanych 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.

Co zrobić, jeśli chcę zabezpieczyć moje REST API za pomocą JWT-token, który jest pobierany z logowania? Muszę skonfigurować dwie konfiguracje przestrzeni nazw tagi http, prawa? Inne dla / login z UsernamePasswordAuthenticationFilter, a inne dla REST url ' s, z niestandardowym JwtAuthenticationFilter.

Nie, Nie jesteś do tego zmuszony. Możesz zadeklarować zarówno UsernamePasswordAuthenticationFilter, jak i JwtAuthenticationFilter w tym samym elemencie http, ale zależy to od konkretnego zachowania każdego z tych filtrów. Oba podejścia są możliwe, a który z nich wybrać finalnie zależy od własnych preferencji.

Czy konfigurowanie dwóch elementów http tworzy dwie springSecurityFitlerChains?

Tak, to prawda

Is UsernamePasswordAuthenticationFilter turned domyślnie wyłączone, dopóki nie zadeklaruję form-login?

Tak, można było to zobaczyć w filtrach podniesionych w każdym z configów, które zamieściłem

Jak zastąpić SecurityContextPersistenceFilter filtrem, który uzyska uwierzytelnienie z istniejącego tokenu JWT, a nie jsessionid?

Możesz uniknąć SecurityContextPersistenceFilter, konfigurując strategię sesji W <http:element>. Po prostu skonfiguruj tak:

<security:http create-session="stateless" >

Lub, W tym przypadku można go zastąpić innym filtrem, w ten sposób wewnątrz elementu <security:http>:

<security:http ...>  
   <security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>    
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />

EDIT:

Jedno pytanie dotyczące "możesz mieć więcej niż jeden mechanizm przetwarzania uwierzytelniania skonfigurowany w tym samym FilterchainProxy". Czy to drugie nadpisze uwierzytelnianie wykonywane przez pierwsze, jeśli deklaruje się wiele filtrów uwierzytelniania (implementacja Spring)? Jak to się ma do posiadania wielu dostawców uwierzytelniania?

To w końcu zależy od implementacji każdego filtra, ale prawdą jest, że ostatnie filtry uwierzytelniania są przynajmniej w stanie zastąpić wszelkie wcześniejsze uwierzytelnianie dokonane przez poprzednie filtry.

Ale to nie będzie konieczne. Mam kilka przypadków produkcyjnych w zabezpieczonych usługach REST, gdzie używam swego rodzaju tokena autoryzacyjnego, który może być dostarczony zarówno jako nagłówek Http, jak i wewnątrz ciała żądania. Konfiguruję więc dwa filtry, które odzyskują ten token, w jednym przypadku z nagłówka Http i drugiego z ciała żądania własnego żądania rest. Prawdą jest, że jeśli jedno żądanie http dostarcza token uwierzytelniania zarówno jako nagłówek Http, jak i wewnątrz ciała żądania, oba filtry spróbują uruchomić mechanizm uwierzytelniania delegując go do menedżera, ale można go łatwo uniknąć po prostu sprawdzając, czy żądanie jest już uwierzytelnione na początku metody doFilter() każdego filtra.

Posiadanie więcej niż jednego uwierzytelnienia filtr jest związany z posiadaniem więcej niż jednego dostawcy uwierzytelniania, ale nie wymuszaj tego. W przypadku, gdy wystawiłem 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 w przeciwieństwie do tego, ja też mam scenariusz, w którym publikuję tylko jeden UsernamePasswordAuthenticationFilter, ale poświadczenia użytkownika mogą być zawarte w DB lub LDAP, więc mam dwa UsernamePasswordAuthenticationToken dostawców wspierających, a AuthenticationManager deleguje wszelkie próby uwierzytelniania z filtra do dostawców secually w celu potwierdzenia poświadczeń.

Myślę więc, że jasne jest, że ani ilość filtrów uwierzytelniania nie określa ilości dostawców uwierzytelniania, ani Ilość dostawców nie określa ilości filtrów.

Ponadto dokumentacja stwierdza SecurityContextPersistenceFilter jest odpowiedzialny za czyszczenie SecurityContext, co jest ważne ze względu na łączenie wątków. Jeśli go pominę lub dostarczę niestandardową implementację, muszę wdrożyć czyszczenie ręcznie, prawda? Czy jest więcej podobnych gotcha podczas dostosowywania łańcucha?

Wcześniej nie zaglądałem dokładnie do tego filtra, ale po twoim ostatnim pytaniu sprawdzałem jego implementację i jak zwykle wiosną prawie wszystko można skonfigurować, rozbudować lub nadpisane.

Securitycontextpersistencefilter deleguje wSecurityContextRepository implementację wyszukiwania SecurityContext. Domyślnie używany jest HttpSessionSecurityContextRepository, ale można to zmienić za pomocą jednego z konstruktorów filtra. Więc może lepiej napisać SecurityContextRepository, który pasuje do Twoich potrzeb i po prostu skonfigurować go w filtrze SecurityContextPersistenceFilter, ufając, że to udowodnione zachowania, a nie zacząć wszystko od zera.
 108
Author: jlumietu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-28 12:30:54

UsernamePasswordAuthenticationFilter jest używany tylko dla /login, a ostatnie filtry nie są?

Nie, UsernamePasswordAuthenticationFilter rozszerza AbstractAuthenticationProcessingFilter, a to zawiera RequestMatcher, co oznacza, że możesz zdefiniować swój własny adres URL przetwarzania, ten filtr obsługuje tylko RequestMatcher pasuje do adresu URL żądania, domyślny adres URL przetwarzania to /login.

Późniejsze filtry nadal mogą obsłużyć żądanie, jeśli UsernamePasswordAuthenticationFilter wykona chain.doFilter(request, response);.

Więcej szczegółów o core fitlers

Czy element przestrzeni nazw form-login automatycznie skonfigurować te filtry?

UsernamePasswordAuthenticationFilter jest tworzony przez <form-login>, są to Standardowe aliasy filtrów i kolejność

Czy każde żądanie (uwierzytelnione lub nie) dociera do FilterSecurityInterceptor dla adresu URL bez logowania?

Zależy od tego, czy poprzednie fitlery odniosą sukces, ale FilterSecurityInterceptor jest ostatnim fitlerem normalnie.

Czy konfigurowanie dwóch elementów http tworzy dwie springSecurityFitlerChains?

Tak, każdy fitlerChain ma RequestMatcher, Jeśli RequestMatcher pasuje do żądania, żądanie będzie obsługiwane przez fitlerów w łańcuchu fitler.

Domyślne RequestMatcher pasuje do wszystkich żądań, jeśli nie skonfigurujesz wzorca lub możesz skonfigurować konkretny adres url (<http pattern="/rest/**").

Jeśli chcesz dowiedzieć się więcej o fitlerach, myślę, że możesz sprawdzić kod źródłowy w spring security. doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

 3
Author: chaoluo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-05 09:30:15