Как работает цепочка фильтров безопасности Spring

Я понимаю, что Spring security строится на цепочке фильтров, которые перехватывают запрос, обнаруживают (отсутствие) аутентификации, перенаправляют на точку входа аутентификации или передают запрос в службу авторизации и в конечном итоге позволяют запросу либо попасть в сервлет, либо бросить исключение безопасности (неавторизованное или несанкционированное). DelegatingFitlerProxy склеивает эти фильтры вместе. Для выполнения своих задач эти службы фильтрации доступа, такие как UserDetailsService и AuthenticationManager.

ключевые фильтры в цепочке (в порядке)

  • SecurityContextPersistenceFilter (восстанавливает аутентификацию из JSESSIONID)
  • UsernamePasswordAuthenticationFilter (выполняет аутентификацию)
  • ExceptionTranslationFilter (поймать исключения безопасности из FilterSecurityInterceptor)
  • FilterSecurityInterceptor (может бросить исключения аутентификации и авторизации)

Я смущен тем, как используются эти фильтры. Разве что для весны предусмотрена форма-login,UsernamePasswordAuthenticationFilter используется только для /логин, а последние фильтры Нет? Делает форма входа элемент пространства имен автоматически настраивает эти фильтры? Достигает ли каждый запрос (аутентифицированный или нет)FilterSecurityInterceptor для не-входа URL-адрес?

что делать, если я хочу защитить свой REST API с помощью JWT-token, который извлекается из логина? я должен настроить две конфигурации пространства имен http теги, прав? Другой /логин С UsernamePasswordAuthenticationFilter, и еще один для остальных url-адресов, с пользовательским JwtAuthenticationFilter.

делает настройку two http элементы создают два springSecurityFitlerChains? Is UsernamePasswordAuthenticationFilter выключен по умолчанию, пока я не объявлю form-login? Как заменить SecurityContextPersistenceFilter С одной, который получит Authentication из существующих JWT-token, а не JSESSIONID?

2 ответов


цепь фильтра безопасностью весны очень сложный и гибкий двигатель.

ключевые фильтры в цепочке (в порядке)

  • SecurityContextPersistenceFilter (восстанавливает аутентификацию из JSESSIONID)
  • UsernamePasswordAuthenticationFilter (выполняет аутентификацию)
  • ExceptionTranslationFilter (поймать исключения безопасности из FilterSecurityInterceptor)
  • FilterSecurityInterceptor (может вызывать исключения аутентификации и авторизации)

смотреть на!--39-->текущая стабильная версия 4.2.1 документация, раздел Фильтр 13.3 Заказ вы могли видеть организацию фильтра всей цепочки фильтров:

Фильтр 13.3 Заказ

порядок определения фильтров в цепочке очень важен. Независимо от того, какие фильтры вы фактически используя, заказ должен быть следующим:

  1. ChannelProcessingFilter, потому что может потребоваться перенаправление на другой протокол

  2. SecurityContextPersistenceFilter, поэтому SecurityContext может быть настроен в SecurityContextHolder в начале веб-запроса, и любые изменения SecurityContext можно скопировать в HttpSession когда веб-запрос заканчивается (готов к использованию со следующим веб-запросом)

  3. ConcurrentSessionFilter, потому что он использует функцию SecurityContextHolder и должен обновить SessionRegistry, чтобы отразить текущие запросы от участника

  4. механизмы обработки аутентификации - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter etc-так, что SecurityContextHolder сможет быть изменен, чтобы содержать допустимый токен запроса аутентификации

  5. на SecurityContextHolderAwareRequestfilter, если вы используете его для установки Spring Security aware HttpServletRequestWrapper в свой контейнер сервлета

  6. на JaasApiIntegrationFilter, если a JaasAuthenticationToken находится в SecurityContextHolder это будет обрабатывать FilterChain как Тема в JaasAuthenticationToken

  7. RememberMeAuthenticationFilter, так что если ранее механизм обработки аутентификации не обновлял SecurityContextHolder, и запрос представляет cookie, который позволяет помните-мне услуги место, соответствующий запомненный объект аутентификации будет помещен там

  8. AnonymousAuthenticationFilter, так что если ранее механизм обработки аутентификации не обновлял SecurityContextHolder, анонимный объект аутентификации будет помещен туда

  9. ExceptionTranslationFilter, чтобы поймать любые исключения безопасности Spring так что либо ответ на ошибку HTTP может быть возвращен, либо соответствующий AuthenticationEntryPoint может быть запущен

  10. FilterSecurityInterceptor, чтобы защитить веб-URIs и вызвать исключения, когда доступ запрещен

теперь я попытаюсь продолжить ваши вопросы один за другим:

я смущен тем, как используются эти фильтры. Это из-за весны? предоставлена форма-вход, UsernamePasswordAuthenticationFilter используется только для / login, а последние фильтры Нет? Пространство имен form-login элемент автоматически настраивает эти фильтры? Каждый запрос (аутентифицируется или нет) reach FilterSecurityInterceptor для входа в систему URL-адрес?

после настройки <security-http> Раздел, для каждого из них вы должны по крайней мере предоставить один механизм аутентификации. Это должен быть один из фильтров, которые соответствуют группе 4 в заказе фильтра 13.3 раздел из документации Spring Security, на которую я только что ссылался.

это минимальный допустимый элемент безопасности: http, который можно настроить:

<security:http authentication-manager-ref="mainAuthenticationManager" 
               entry-point-ref="serviceAccessDeniedHandler">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>

просто делая это, эти фильтры настроены в прокси-сервере цепочки фильтров:

{
        "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"
    }

Примечание: я получаю их, создавая простой RestController, который @Autowires FilterChainProxy и возвращает его содержимое:

    @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;
    }

здесь мы могли видеть это, просто объявив <security:http> элемент с одной минимальной конфигурацией включены все фильтры по умолчанию, но ни один из них не имеет типа аутентификации (4-я группа в разделе заказа фильтра 13.3). Таким образом, это на самом деле означает, что просто объявив security:http элемент, SecurityContextPersistenceFilter, ExceptionTranslationFilter и FilterSecurityInterceptor настроены автоматически.

фактически, один механизм обработки аутентификации должен быть настроен, и даже бобы пространства имен безопасности обрабатывают утверждения для это, бросая ошибку во время запуска, но ее можно обойти, добавив атрибут entry-point-ref в <http:security>

если я добавлю basic <form-login> конфигурации, таким образом:

<security:http authentication-manager-ref="mainAuthenticationManager">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
    <security:form-login />
</security:http>

теперь filterChain будет выглядеть так:

{
        "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"
    }

теперь, это два фильтра org.springframework.безопасность.сеть.идентификация.UsernamePasswordAuthenticationFilter и орг.springframework.безопасность.сеть.идентификация.пользовательский интерфейс.DefaultLoginPageGeneratingFilter создаются и настраиваются в FilterChainProxy.

Итак, вопросы:

это то, что для весны предоставляется форма-логин, UsernamePasswordAuthenticationFilter используется только для / login и последняя фильтров нет?

Да, он используется для завершения механизма обработки входа в систему, если запрос соответствует Url-адрес UsernamePasswordAuthenticationFilter. Этот url-адрес можно настроить или даже изменить, чтобы он соответствовал каждому запросу.

у вас также может быть несколько механизмов обработки аутентификации, настроенных в одном FilterchainProxy (например, HttpBasic, CAS и т. д.).

элемент пространства имен form-login автоматически настраивает эти фильтры?

нет, элемент form-login настраивает UsernamePasswordAUthenticationFilter, и если вы не предоставляете url-адрес страницы входа, он также настраивает организацию.springframework.безопасность.сеть.идентификация.пользовательский интерфейс.DefaultLoginPageGeneratingFilter, который заканчивается в простую страницу входа автоматически.

другие фильтры автоматически настраиваются по умолчанию, просто создавая <security:http> элемент без .

каждый запрос (аутентифицированный или нет) достигает FilterSecurityInterceptor для url-адреса без входа?

запрос должен достичь его, так как это элемент, который заботится о том, имеет ли запрос права на доступ к запрошенному url-адресу. Но некоторые из фильтров, обработанных ранее, могут остановить обработку цепочки фильтров, просто не вызывая FilterChain.doFilter(request, response);. Например, фильтр CSRF может остановить обработку цепочки фильтров, если запрос не имеет параметра csrf.

что делать, если я хочу защитить свой REST API с помощью JWT-токена, который извлекается из login? Я должен настроить два пространства имен настройка http-тегов, прав? Другой для / войти с UsernamePasswordAuthenticationFilter, и еще один для остальных url-адресов, с пользовательским JwtAuthenticationFilter.

нет, вы не обязаны делать это таким образом. Вы можете объявить оба UsernamePasswordAuthenticationFilter и JwtAuthenticationFilter в том же элементе http, но это зависит от конкретного поведения каждого из этих фильтров. Оба подхода возможны, и какой из них выбрать или зависит от собственных предпочтений.

настройка двух элементов http создать две springSecurityFitlerChains?

Да, это правда

отключен ли UsernamePasswordAuthenticationFilter по умолчанию, пока я не объявлю form-login?

Да, вы могли видеть это в фильтрах, поднятых в каждом из конфигураций, которые я опубликовал

как заменить SecurityContextPersistenceFilter на один, который будет получать аутентификацию от существующего JWT-токена, а не JSESSIONID?

вы можете избежать SecurityContextPersistenceFilter, просто настроив сессии стратегии на <http:element>. Просто настройте так:

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

или, в этом случае вы можете перезаписать его другим фильтром, таким образом, внутри <security:http> элемент:

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

EDIT:

один вопрос о "у вас тоже может быть настроено несколько механизмов обработки аутентификации в том же FilterchainProxy". Будет ли последний перезаписывать аутентификацию, выполняемую первым, при объявлении нескольких (реализация Spring) фильтров аутентификации? Как это связано с наличием нескольких поставщиков аутентификации?

это, наконец, зависит от реализации каждого фильтра, но это правда, что последние фильтры аутентификации, по крайней мере, могут перезаписать любую предыдущую аутентификацию, в конечном итоге сделанную предыдущим фильтры.

но это не обязательно случится. У меня есть некоторые производственные случаи в защищенных службах REST, где я использую своего рода маркер авторизации, который может быть предоставлен как в качестве HTTP-заголовка, так и внутри тела запроса. Поэтому я настраиваю два фильтра, которые восстанавливают этот токен, в одном случае из заголовка Http, а другой из тела запроса собственного запроса rest. Это правда, что если один http-запрос предоставляет этот токен аутентификации как как HTTP-заголовок, так и внутри запроса тело, оба фильтра будут пытаться выполнить механизм аутентификации, делегирующий его менеджеру, но его можно легко избежать, просто проверяя, аутентифицирован ли запрос уже в начале doFilter() метод каждого фильтра.

наличие более одного фильтра аутентификации связано с наличием более одного поставщика аутентификации,но не заставляйте его. В случае, который я предоставил раньше, у меня есть два фильтра аутентификации, но у меня есть только один поставщик аутентификации, поскольку оба фильтра создают один и тот же тип объекта проверки подлинности, в обоих случаях диспетчер проверки подлинности делегирует его одному и тому же поставщику.

и напротив этого, у меня тоже есть сценарий, в котором я публикую только один UsernamePasswordAuthenticationFilter, но учетные данные пользователя могут содержаться в DB или LDAP, поэтому у меня есть два поставщика поддержки UsernamePasswordAuthenticationToken, и AuthenticationManager делегирует любую попытку аутентификации от фильтра к поставщики secuentially для проверки учетных данных.

Итак, я думаю, ясно, что ни количество фильтров аутентификации не определяет количество поставщиков аутентификации, ни количество провайдеров не определяет количество фильтров.

кроме того, в документации говорится, что SecurityContextPersistenceFilter отвечает за очистку SecurityContext, что важно из-за объединения потоков. Если я опущу его или предоставлю пользовательскую реализацию, я должен реализовать уборка вручную, да? Есть еще похожие попался при настройке цепи?

я не смотрел внимательно в этот фильтр раньше, но после вашего последнего вопроса я проверял его реализацию, и, как обычно весной, почти все может быть настроено, расширено или перезаписано.

на SecurityContextPersistenceFilter делегаты в SecurityContextRepository реализация поиск SecurityContext. По умолчанию HttpSessionSecurityContextRepository, но это может быть изменено с помощью одного из конструкторов фильтра. Поэтому, возможно, лучше написать SecurityContextRepository, который соответствует вашим потребностям, и просто настроить его в SecurityContextPersistenceFilter, доверяя его доказанному поведению, а не начинать делать все с нуля.


UsernamePasswordAuthenticationFilter используется только для /login, а последние фильтры Нет?

нет, UsernamePasswordAuthenticationFilter выходит AbstractAuthenticationProcessingFilter, и содержит RequestMatcher, это означает, что вы можете определить свой собственный url-адрес обработки, этот фильтр обрабатывает только RequestMatcher соответствует url-адресу запроса, url-адрес обработки по умолчанию /login.

более поздние фильтры все еще могут обрабатывать запрос, если UsernamePasswordAuthenticationFilter выполняет chain.doFilter(request, response);.

подробнее о ядро fitlers

элемент пространства имен form-login автоматически настраивает эти фильтры?

UsernamePasswordAuthenticationFilter создано <form-login>, это стандартные псевдонимы фильтра и заказ

каждый запрос (аутентифицированный или нет) достигает FilterSecurityInterceptor для url-адреса без входа?

это зависит от того, прежде чем fitlers успешны, но FilterSecurityInterceptor последний фильтровать обычно.

настройка двух элементов http создает две springSecurityFitlerChains?

да, каждый fitlerChain имеет RequestMatcher, если RequestMatcher соответствующий запрос, запрос будет обрабатываться fitlers в фильтровать цепи.

по умолчанию RequestMatcher соответствует всем запросам, если вы не настраиваете шаблон, или вы можете настроить конкретный url (<http pattern="/rest/**").

если вы хотите узнать больше о fitlers, я думаю вы можете проверить исходный код в spring security. doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)