SPRING-SOURCE.RU

Анатомия незащищенного приложения

  1. Узнаем ключевые концепции защищенных приложений
  2. Реализуем основной уровень защиты, используя быструю настройку в Spring Security
  3. Разберем высокий уровень архитектуры Spring Security
  4. Исследуем стандартную конфигурацию и параметры для аутентификации и авторизации
  5. Рассмотрим язык выражений Spring в контроле доступа Spring Security

Аутентификация

Вообще, существует множество форм аутентификации, которые могут быть применены к проблемам software и hardware безопасности, где каждый имеет свои преимущества и недостатки. Мы рассмотрим некоторые из этих методов и как они реализуются Spring Security.

Spring Security расширяет стандартную идею безопасности Java аутентификации принципала – principal (java.security.Principal), который однозначно представляет любую аутентифицированную сущность. В большинстве случаев, с использованием Spring Security, принципал будет просто представлять одного пользователя, и так, когда мы используем слово принципал, вы можете приравнивать его к слову "user".

Авторизация

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

Первое – это связывание аутентифицированного принципала к одному или более полномочиям – authorities (часто называются ролями – roles). Например, случайный пользователь web сайта может быть рассмотрен как пользователь имеющий полномочия посетителя, тогда как администратор сайта может быть отнесен к пользователю с административными полномочиями.

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

Spring Security

Мы можем видеть, что компонент, называемый менеджером управления доступом - access decision manager отвечает за определение того, имеет ли принципал соответствующий уровень доступа, на основе совпадения между полномочиями, которыми обладает принципал и полномочиями требуемыми ресурсом.

Давайте попробуем добавить основную реализацию Spring Security в наше web приложение, а потом еще раз рассмотрим аутентификацию и авторизацию, но более подробно.

Защита вашего приложения за три простых шага

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

И так, давайте начнем с нашего незащищенного приложения и при помощи трех действий переделаем его в защищенное с элементарной аутентификацией username и password. Эта аутентификация служит для иллюстрирования шагов, помогающих включать в нашем приложении Spring Security. Вы увидите в этом подходе очевидные недостатки, которые мы исправим за счет дополнительных тонкостей конфигурации.

Выполним настройку XML конфигурационного файла Spring Security

Первым шагом, лежащим в основе процесса конфигурации - это создание конфигурационного файла XML, представляющим все компоненты Spring Security необходимых для покрытия стандартных web запросов.

XML файл создается в WEB-INF директории с именем dogstore-security.xml

                        
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:beans="http://www.springframework.org/schema/beans"
  xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/
spring-security-3.0.xsd">
	<http auto-config="true">
	  <intercept-url pattern="/*" access="ROLE_USER" />
	</http>
	<authentication-manager alias="authenticationManager">
	  <authentication-provider>
	    <user-service>
	      <user authorities="ROLE_USER" name="guest" password="guest" />
	    </user-service>
	  </authentication-provider>
	</authentication-manager>
</beans:beans>
		

Это единственная Spring Security конфигурация необходимая для получения защищенного web приложения с минимальной стандартной конфигурацией. Этот стиль конфигурации, использует конкретный Spring Security диалект известный как стиль пространства имен security, идущего после XML пространства имен (http://www.springframework.org/schema/security). В будущем мы будем обсуждать альтернативные стили конфигурации, используя традиционное связывание бинов, а также пространства имен XML.

Добавление Spring DelegatingFilterProxy в ваш web.xml файл

Spring Security оказывает основное влияние на наше web приложение через серию ServletRequest фильтров (мы рассмотрим их чуть позже, когда будем обсуждать Spring Security архитектуру). Можно представить себе эти фильтры как бутерброд, который окружает каждый запрос к вашему приложению.

o.s.web.filter.DelegatingFilterProxy - это фильтр-сервлет, который позволяет SpringSecurity окружить все запросы и гарантировать, что они защищены надлежащим образом.

Заметка: DelegatingFilterProxy поставляется с Spring framework, а не является частью безопасности. Этот фильтр в основном используется с Spring web приложениями.

Настраиваем ссылки к этому фильтру через добавление следующего кода к нашему web.xml файлу, сразу после Spring MVC <servlet- mapping> элемента:

                        
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filterclass>
    org.springframework.web.filter.DelegatingFilterProxy
  </filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
		

Здесь мы применяем ServletRequest фильтр и конфигурируем его для обработки запросов совпадающих с заданным URL шаблоном (/*). Получается, что каждый запрос будет обрабатываться этим фильтром.

Как вы могли заметить, это не имеет ничего общего с Spring Security конфигурацией, которую мы выполнили в dogstore-security.xml раньше. Чтобы связать конфигурации, мы добавим к конфигурационному XML файлу ссылку.

Добавляем ссылки Spring Security XML в web.xml

В зависимости от того как вы сконфигурировали свое Spring web приложение, вы имеете или не имеете точную ссылку к XML конфигурационному файлу в web.xml (описание развертывания). По умолчанию Spring Web ContrextLoaderListener ищет XML конфигурационный файл с тем же именем, что и Spring Web сервлет. Давайте сделаем изменения в нашем конфигурационном файле для того, чтобы базовый уровнь Spring Security смог нормально функционировать.

Первым делом, мы дожлны внимательно посмотреть использует ли сайт Spring MVC автоматический поиск конфигурационного файла XML. Для того чтобы сделать это мы должны посмотреть имя сервлета в web.xml файле:

                        
<servlet>
  <servlet-name>dogstore</servlet-name>
  <servletclass>
    org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
		

Имя сервлета (<servlet-name>) - dogstore, Spring Convention over Configuration (CoC) правила будут искать XML конфигурационный файл под названием dogstore-servlet.xml в WEB-INF.

Кроме того, можно объявить дополнительные конфигурационные файлы spring ApplicationContext, которые будут загружены до связанного с SpringMVC сервлета. Это достигается за счет использования Spring o.s.web.context.ContextLoaderListener для создания ApplicationContext независимо от SpringMVC ApplictionContext.

Расположение XML файлов используемых для конфигурации ContextLoaderListener перечисляется в <context-param> элементе вашего web.xml файла:

                        
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    /WEB-INF/dogstore-base.xml
</param-value>
</context-param>
		

В dogstore-base.xml файле у нас будет хранится информация касательно источников данных, сервисных бинов и так далее. Теперь мы добавим дополнительную ссылку к новому XML файлу, в котором будет храниться информация по Spring Security.

                        
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    /WEB-INF/dogstore-security.xml
    /WEB-INF/dogstore-base.xml
</param-value>
</context-param>
		

После этих действий можно попробовать наше web приложение, набрав в адресной строке http://localhost:8080/JBCPPets/home.do. Резальтат должен быть следующим:

Spring Security

Хорошая работа! Мы сделали с вами базовый слой безопасности в вашем web приложении, всего за три шага, используя Spring Security. Теперь, если вы хотите, то можете войти с систему, используя guest в качестве имени и пароля.

Восполним пробелы

Остановимся и подумаем о том, что мы только что сделали. Создание базового профиля нашей реализации Spring Security было ослепительно быстро. Spring Security предоставил нам страницу для входа в систему, аутентификацию с именем пользователя и паролем, а также автоматический перехват URL. Тем не менее, есть определенные пробелы между автоматически предоставленной конфигурацией и тем, что нам требуется:

Общие проблемы

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

Усложнение безопасности: архитектура безопасных web запросов

Конфигурация в три шага, которую мы иллюстрировали ранее была сделана очень быстро; спасибо Spring Security базовой конфигурации и хорошей реализации аутентификации, которую мы включили через объявление auto-config атрибут на <http> элементе.

К сожалению, базовая конфигурация не подходит для более сложных web приложений. Многие пользователи Spring Security сталкиваются с проблемами при выходе за рамки базовой конфигурации, они не понимают архитектуры продукта и того, как работают все факторы вместе, определяя единое целое.

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

Spring Security

Как обрабатываются запросы?

Архитектура Spring Security в значительной мере опирается на использование делегатов (delegates) и фильтров сервлета (servlet filters), обеспечивая функциональность слоев вокруг содержания запросов web приложения.

Фильтры сервлета (классы, которые реализуют интерфейс javax.servlet.Filter) используются для перехвата пользовательских запросов и выполнения пре- или пост- обработки, или для перенаправления запроса целиком, в зависимости от функции фильтра сервлета. Последний пункт назначения сервлета это Spring MVC dispatcher сервлет. Следующая диаграмма иллюстрирует, как фильтр сервлета захватывает (обертывает) пользовательский запрос:

Spring Security

Автоматическая конфигурация в Spring Security устанавливает серию из десяти фильтров, которые последовательно применяются через использование цепи фильтров сервлета Java EE. Цепочка фильтров (filter chain) - это понятие Java EE Servlet API определенное javax.servlet.FilterChain интерфейсом, который позволяет web приложению направить и применить эту цепочку фильтров сервлета к любому запросу.

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

Spring Security

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

Spring Security берет понятие цепочки фильтров и реализует свою абстракцию - VirtualFilterChain, которая обеспечивает дополнительную функциональность, позволяя FilterChain динамически строиться на основе URL шаблонов, определенных в Spring Security XML конфигурации (в отличии от стандартного Java EE FliterChain, который определяется на основе описания развертывания web приложения - web.xml).

Заметка: Фильтры сервлета могут использоваться для различных целей, кроме, как можно увидеть из названия, фильтрации (блокировка запроса). В действительности, многие фильтры сервлета функционируют как proxy AOP стиля- перехвата web запросов в среде запуска web приложения, то есть их функциональность происходит перед или после любого web запроса к контейнеру сервлета.

Этот многоцелевой характер фильтров в Spring Security, как и многих других не взаимодействует на прямую с пользовательским запросом.

Далее. Опция автоматической конфигурации устанавливает 10 фильтров Spring Security. Понимание того, что эти фильтры делают по умолчанию, и где и как они настраиваются, имеет решающее значение для профессиональной работы со Spring Security.

Эти фильтры, и в порядке в котором они применяются, описываются в следующей таблице. Не беспокойтесь, если вы не сразу поймете смысл этих фильтров, по ходу мы еще не раз будем их описывать.

Имя фильтра Описание
o.s.s.web.context.SecurityContextPersistenceFilter Он ответственный за загрузку и хранение SecurityContext из SecurityContextRepository. SecurityContext представляет защищенного пользователя, аутентификационную сессию (сеанс).
o.s.s.web.authentication.logout.LogoutFilter Он смотрит за запросами к виртуальному URL указанному как logout URL (по умолчанию /j_spring_security_logout).
o.s.s.web.authentication.UsernamePasswordAuthenticationFilter Он смотрит за запросами к виртальному URL используемому для аутентификации основанной на формах, с указанием пользователя и пароля (по умолчанию /j_spring_security_check) и пытается аутентифицировать пользователя, если есть совпадение.
o.s.s.web.authentication.ui.DefaultLoginPageGeneratingFilter Он смотрит за запросами к виртальному URL используемому для аутентификации основанной на формах или OpenID (по умолчанию /spring_security_login) и генерирует HTML, который визуализирует форму для входа.
o.s.s.web.authentication.www.BasicAuthenticationFilter Он смотрит за HTTP заголовками аутентификации и обрабатывает их.
o.s.s.web.savedrequest.RequestCacheAwareFilter Он используется после удачного входа для восстановления оригинального запроса пользователя, который был перехвачен аутентфикационным запросом.
o.s.s.web.servletapi.SecurityContextHolderAwareRequestFilter Он обертывает HttpServletRequest с подклассом (o.s.s.web. servletapi.SecurityContextHolderAwareRequestWrapper), который расширяет HttpServletRequestWrapper. Он предоставляет дополнительный контекст к процессору запроса далее вниз по цепочке фильтров.
o.s.s.web.authentication.AnonymousAuthenticationFilter Если пользователь не аутентифицирован, в то время когда фильтр вызван, токен аутентификации связанного с запросом, показывает, что пользователь анонимный.
o.s.s.web.session.SessionManagementFilter Он обрабатывает слежение за сессиями основанных на аутентифицированных принципалах, помагает быть уверенным в том, что все сессии связанные с одним принципалом отслеживаются.
o.s.s.web.access.ExceptionTranslationFilter Этот фильтр обрабатывает маршрутизацию по умолчанию и делегирует ожидаемые исключения, которые возникают в процессе обработки защищенных запросов.
o.s.s.web.access.intercept.FilterSecurityInterceptor Он облегчает определение авторизации и решений связанных с контролем доступа, делегируя решения за авторизацию к AccessDecisionManager.

Spring Security имеет приблизительно 25 фильтров, которые могут быть применены при различных нуждах для обработки поведения пользовательских запросов. Конечно, вы можете добавить свои фильтры сервлета через реализацию javax.servlet.Filter.

Запомните, что фильтры, перечисленные в таблице, создаются автоматически, когда вы указываете атрибут auto-config в вашем XML файле.

Что делает auto-config за сценой?

В Spring Security 3, auto-config используется только для автоматического обеспечения следующими тремя функциями связанными с аутентификацией:

Как пользователь проходит аутентификацию?

Когда пользователь предоставляет свои учетные данные через форму для входа, эти учетные данные должны сверяться в отношении credential store (хранение учетных данных) для того, чтобы продолжить любые дальнейшие шаги в нашей защищенной системе. Проверка учетных данных представлена включением серий логических компонентов, которые используются для инкапсуляции аспектов аутентификационного процесса.

Мы будем работать на примере нашего образца, где используется форма для входа с пользователем и паролем. Имейте ввиду, однако, что общая архитектура аутентификации такая же, как аутентфикация основанная на форме для входа, с использованием внешнего поставщика аутентификации такого как Central Authentication Service (CAS) или учетные данные пользователя хранятся в базе данных или в LDAP директории.

Важные интерфейсы связанные с взаимодействием с аутентификацией показаны на диаграмме высокого уровня:

Spring Security

На этой диаграмме вы можете видеть три главных компонента, которые ответственны за всю тяжелую работу:

Имя интерфейса Описание / роль
AbstractAuthenticationProcessingFilter Он находится в web аутентификационных запросах. Обрабатывает входящие запросы содержащие POST формы, SSO информацию, или другие учетные данные предоставленные пользователем. Создает частично заполненный Authentication объект для передачи пользовательских учетных данных вдоль цепочки ответственности.
AuthenticationManager Он ответственен за проверку полномочий пользователя, и, либо выбрасывает исключение (в данном случае – провал аутентификации), или полностью заполняет Authentication объект, в особенности включая информацию о полномочиях (authority information).
AuthenticationProvider Он ответственен за доставку проверки полномочий к AuthenticationManager. Некоторые AuthenticationProvider частично основывают свое одобрение полномочий на источниках учетных данных, таких как база данных.

o.s.s.core.Authentication – это интерфейс с которым вы будете взаимодействовать очень часто, потому что он хранит информацию об идентификаторе пользователя (например, username), учетные данные (например, пароль), и одно или более полномочий (o.s.s.core.GrantedAuthority), которыми обладает пользователь. Разработчик обычно использует Authentication объект для извлечения деталей об аутентифицированном пользователе, или в пользовательской реализации он/она будут наращивать Authentication объект дополнительной информацией, которая требуется для другого приложения.

Доступны следующие методы в Authentication интерфейсе:

Сигнатура метода Описание
Object getPrincipal() Возвращает уникальный идентификатор принципала (например, имя пользователя).
Object getCredentials() Возвращает учетные данные принципала.
List<GrantedAuthority>getAuthorities() Возвращает набор полномочий, которые имеет принципал, как определено в хранилище аутентификации.
Object getDetails() Возвращает в зависимости от провайдера детали о принципале.

Как вы возможно заметили, Authentication интерфейс имеет несколько методов, которые просто возвращают java.lang.Object. Это сделать довольно сложно: знать во время компиляции, какой тип объекта вы в действительности получили обратно от вызова метода у Authentication объекта.

Возьмите на заметку, что AuthenticationProvider не может использоваться напрямую или (referenced by the AuthenticationManager интерфейс). Однако, Spring Security поставляется только с одной конкретной реализацией (плюс подкласс) AuthenticationManager, o.s.s.authentication.ProviderManager, которая использует один или более AuthenticationProvider реализаций. Так как использование AuthenticationProvider распространено и хорошо интегрировано в ProviderManager, важно рассмотреть как он работает в более общих сценариях с базовой конфигурацией:

Spring Security

Давайте возьмем абстрактный процесс, рассматриваемый в диаграмме высокого уровня и представим работу этой конкретной реализации аутентификации основанной на форме. Вы можете видеть, что UsernamePasswordAuthenticationFilter ответственен (через делегирование от абстрактного суперкласса) за создание UsernamePasswordAuthenticationToken (реализация Authentication интерфейса), частично заполняет ее информацией из HttpServletRequest. Но где взять имя пользователя и пароль?

Что такое spring_security_login и как его получить?

Как вы могли заметить, когда вы перешли на домашнюю страницу нашего сайта, то сразу были перенаправлены на http://localhost:8080/JBCPPets/spring_security_login.

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

Давайте взглянем в исходный код HTML этой формы для того чтобы выяснить, что ожидает UsernamePasswordAuthenticationFilter:

                        
<form name='f' action='/JBCPPets/j_spring_security_check' method='POST'>
  User:
    <input type='text' name='j_username' value=''>
      Password:
      <input type='password' name='j_password' />
      <input name="submit" type="submit" />
      <input name="reset" type="reset" />
</form>
		

Мы видим здесь необычные имена (j_username и j_password) назначенные к полям формы для имени пользователя и пароля, и действие формы j_spring_security_check.

Имена полей формы определены в UsernamePasswordAuthenticationFilter, и берут их аналогии в Java Servlet 2.x спецификации, которая требует чтобы имена были именно такими, плюс действие формы j_security_check. Другими словами это просто стандарты.

UsernamePasswordAuthenticationFilter настраивается с помощью <form-login> подэлемента <http>.

Где проверяются учетные данные?

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

                        
<authentication-manager alias="authenticationManager">
  <authentication-provider>
    <user-service>
      <user authorities="ROLE_USER" name="guest" password="guest" />
    </user-service>
  </authentication-provider>
</authentication-manager>
		

Мы не связывали этот AuthenticationProvider к какой-либо реализации. Помните, что реализация AuthenticationManager по умолчанию поддерживает конфигурацию одной или более реализаций AuthenticationProvider. Объявление <authentication-provider> запускает реализацию o.s.s.authentication.dao.DaoAuthenticationProvider. Объявление элемента <authentication-provider> также автоматически свяжет AuthenticationProvider с сконфигурированным AuthenticationManager.

DaoAuthenticationProvider является AuthenticationProvider, который обеспечивает тонкой оболочкой реализуя AuthenticationProvider интерфейс и затем делегирует реализации интерфейса o.s.s.core.userdetails.UserDetailsService. UserDetailsService ответственен за возвращение реализации o.s.s.core.userdetails.UserDetails.

Если вы посмотрите Javadoc для UserDetails, вы заметите, что он поразительно похож на Authentication интерфейс, рассматриваемый нами ранее. Не пугайтесь, хотя они немного дублируют названия методов и способностей, они имеют разные цели:

Интерфейс Цель
Authentication он хранит идентичность принципала, его пароль, и информацию о контексте (context) аутентификационного запроса. Он также может хранить post-аутентификационную информацию о пользователе (которая может включать экземпляр UserDetails). Обычно не расширяется, за исключением, когда требуется поддержка требований специальных типов аутентификации.
UserDetails предназначен для хранения профиля принципала, включая его имя, электронную почту, номер телефона и так далее. Обычно расширяется для поддержки бизнес требований.

Наше объявление подэлемента <user-service> приводит в действие реализацию o.s.s.core.userdetails.memory.InMemoryDaoImpl реализацию UserDetailsService. Как вы могли догадаться, эта реализация хранит сконфигурированных пользователей в XML файле безопасности в оперативной памяти. Настройки этого сервиса позволяют отключать или блокировать аккаунты.

Давайте визуально посмотрим, как взаимодействуют компоненты DaoAuthenticationProvider обеспечивая поддержкой аутентификации AuthentiocationManager:

Spring Security

Аутентификация очень хорошо настраиваема. Большинство примеров Spring Security используют хранение учетных данных либо в памяти либо используя JDBC (в базе данных) для хранения учетных данных.

Когда хорошая аутентификация становится плохой?

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

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

Как авторизируется запрос?

Заключительный фильтр сервлета в цепочке фильтров Spring Security, FilterSecurityInterceptor, этот фильтр ответственен за решение о том будет ли принят конкретный запрос или отвергнут. В этот момент вызывается фильтр FilterSecurityInterceptor, принципал уже аутентифицирован, и система знает что он действительный пользователь. Помните, что Authentication интерфейс определяет метод (List<GrantedAuthority>getAuthorities()), который возвращает список полномочий для принципала. Процесс авторизации будет использовать информацию из этого метода для определения, для конкретного запроса, будет ли позволен запрос или нет.

Помните, что авторизация это бинарное решение – пользователь либо имеет доступ к защищенным ресурсам либо нет.

В Spring Security, интерфейс o.s.s.access.AccessDecisionManager определяет два простых и логичных метода:

AccessDecisionManager реализация полностью настраиваема через исползование стандартного Spring Bean связывания и ссылок. Реализация AccessDecisionManager по умолчанию обеспечивает механизмом предоставления доступа основанного на AccessDecisionVoter и агрегации голосования. AccessDecisionManager использует Голосующего (Voter) для определения авторизации пользователя.

Голосующий (voter) - это актер в последовательности авторизации чья работа заключается в оценке:

Более часто используемым AccessDecisionVoter предоставляемый Spring Security являет простой RoleVoter, который смотрит конфигурационные атрибуты как простые имена ролей и голоса для предоставления доступа, если пользователь был назначен этой роли. Он будет голосовать если любой ConfigAttribute начинается с ROLE_ префикса.

AccessDecisionManager также ответственен за передачу доступа (передаваемый в код реализации интерфейса ConfigAttribute) к ресурсу, который необходим для голосующего. В случае web URL's, голосующий будет иметь информацию о доступе к ресурсу. Если мы взглянем на наш основной конфигурационный файл, на перехват URL, мы увидим объявленный, как ROLE_USER доступ к ресурсу для пользователя:

                        
<intercept-url pattern="/*" access="ROLE_USER"/>
		

Spring Security позволяет голосующему делать одно из трех решений, чье логиечкое определение связано с константами интерейса o.s.s.access.AccessDecisionVoter.

Тип решения Описание
Grant (ACCESS_GRANTED) предоставление доступа к ресурсу
Deny (ACCESS_DENIED) запрет доступа к ресурсу
Abstain (ACCESS_ABSTAIN) голосующий воздерживается (не может сделать решения) от доступа к ресурсу. Это может случиться по ряду причин: голосующий не обладает информацией, голосующий не может решить запрос данного типа.

Когда мы положим это все вместе, общий поток "проверка аутентификации по умлочанию для web запросов" будет похож на следующую диаграмму:

Spring Security

Настройка агрегации управления доуступом

Spring Security позвляет настраивать AccessDecisionManager в пространстве имен security. Атрибут access-decision-manager-ref в <http> элементе позволяет вам определять Spring Bean ссылку к реализации AccessDecisionManager. Spring Security поставляется с тремя реализациями этого интерфейса, все находятся в пакете o.s.s.access.vote:

Имя класса Описание
AffirmativeBased Если любой голосующий предоставляет доступ, доступ незамедлительно предоставляется, независимо от предыдущих отказов
ConsensusBased Большинством голосов (за или против) принимается решение AccessDecisionManager.
UnanimousBased Все голосующие должны предоставить доступ, иначе доступ запрещен.

Настройка для использования UnanimousBased менеджера управления доступом

Если мы хотим изменить наше приложение для использования UnanimousBased менеджера управления доступом, нам потребуется сделать два изменения. Давайте добавим атрибут access-decision-manager-ref к <http> элементу.

                        
<http auto-config="true"
access-decision-manager-ref="unanimousBased">
		

Это стандартная Spring Bean ссылка, которая соответствует id атрибуту бина. Далее мы объявим бин (в dogstore-base.xml), со знакомым ID именем.

                        
<bean class="org.springframework.security.access.vote.UnanimousBased"
id="unanimousBased">
<property name="decisionVoters">
  <list>
    <ref bean="roleVoter" />
    <ref bean="authenticatedVoter" />
  </list>
</property>
</bean>
<bean class="org.springframework.security.access.vote.RoleVoter"
  id="roleVoter" />
<bean class="org.springframework.security.access.vote.
AuthenticatedVoter"
  id="authenticatedVoter" />
		

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

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

Вот две реализации голосующих, на которых мы ссылаемся:

Имя класса Описание Пример
o.s.s.access.vote.RoleVoter Проверяет что пользователь имеет GrantedAuthority совпадающий с объявленной ролью. Ожидает access атрибут для определения списка имен GrantedAuthority. Префикс ROLE_ ожидается, но явлется не обязательным. access="ROLE_USER,ROLE_ADMIN"
o.s.s.access.vote.AuthenticatedVoter Поддерживает специальные декларации:
IS_AUTHENTICATED_FULLY - позволяет получить доступ, если были поставлены новое имя пользователя и пароль
IS_AUTHENTICATED_REMEMBERED - позволяет получить доступ если пользователь аутентифицировался с "помни меня" функцией
IS_AUTHENTICATED_ANONYMOUSLY - позволяет получить доступ если пользователь является анонимным
access=" IS_AUTHENTICATED_ANONYMOUSLY"

Рассмотрим язык выражений Spring в контроле доступа Spring Security

Альтернативный метод реализации механизма голосования основанного на ролях (RoleVoter) - это использование Spring Expression Language (SpEL) выражения для определения общих правил для голосования. Простой способ для реализации этой особенности - это добавить use-expressions атрибут в <http> конфигурационный элемент:

                        
<http auto-config="true" use-expressions="true">
		

Это дополнение будет изменять поведение access атрибута на перехвате URL. SpEL выражения позволяют использовать спецификацию языка выражений для критерия доступа. Вместо простой строки, такой как ROLE_USER, конфигурационный файл может определять выражения.

Важно заметить, что если вы включаете SpEL через use-expressions атрибут, вы отключаете автоматическую конфигурацию RoleVoter, который понимает объявление ролей в "обычном виде":

                        
<intercept-url pattern="/*" access="ROLE_USER"/>
		

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

                        
<http auto-config="true" use-expressions="true">
  <intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>
</http>