spring security


http://www.seostella.com/ru/article/2012/06/27/vvedenie-v-spring-security-hello-world.html
The source: https://github.com/spring-projects/spring-security - for searching code(and encourage dependencies).
Select your tutorial: 



http://habrahabr.ru/post/111102/

Для экспериментов нужно включить логирование для всего Spring security:
log4j.properties:
log4j.logger.org.springframework.security=ALL
ну и проверить - правильно ли настроен log4j для нашего вебприложения, в частности, должен быть листнер в web.xml: 
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

Итак, создадим дефолтную настройку:
<sec:http auto-config="true" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/secure/**" access="ROLE_USER" />
    <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
</sec:http>

<sec:authentication-manager xmlns="http://www.springframework.org/schema/security">
    <authentication-provider>
        <user-service>
            <user name="admin" password="adminpassword" authorities="ROLE_USER, ROLE_ADMIN" />
            <user name="user" password="userpassword" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</sec:authentication-manager>

И посмотрим логи при запуску:
DEBUG (AbstractSecurityInterceptor.java:159) Validated configuration attributes
 INFO (DefaultSecurityFilterChain.java:28) Creating filter chain: org.springframework.security.web.util.AnyRequestMatcher@1, [org.springframework.security.web.context.SecurityContextPersistenceFilter@130998, org.springframework.security.web.authentication.logout.LogoutFilter@1dae160, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@102679a, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@dfbb43, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@7f3b8a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@11e1bbf, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@1afd9cc, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3e926, org.springframework.security.web.session.SessionManagementFilter@bfd66a, org.springframework.security.web.access.ExceptionTranslationFilter@95f290, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@67fe80]
 INFO (DefaultFilterChainValidator.java:122) Checking whether login URL '/spring_security_login' is accessible with your configuration
DEBUG (DefaultFilterChainValidator.java:140) Default generated login page is in use

Вопросы: кто создает, создается ли по умолчанию, какой тег в конфигурации отвечает за создание этого элемента.
    Как мы видим, конфигуратор создал определенное количество фильтров и проверил, доступные ли для запроса пути, которые он собирается использовать. Еще важный момент: в конфигурации может быть сколько угодно цепочек фильтров, так же как и может быть сколько угодно тегов <http>.  Как происходит создание цепочки фильтров и аутентификаторов в случае, если у нас больше одного корневого тега <http> - это просто другой вопрос, который я не затрагиваю, так как это уже не является простейшей конфируцией. Но об этом нужно знать(дальше станет ясно почему). Теперь еще об фильтрах:
    SecurityContextPersistenceFilter - из документации можно узнать, что этот фильтр отвечает за сохранение аутентификации между запросами(а в его исходнике мы можем увидеть, что он её сохраняет в  SecurityContextRepository, дефолтная имплементация которого сохраняет данные между запросами в сессии (HttpSessionSecurityContextRepository) ). Еще сказано, что этот фильтр должен идти ПЕРЕД любыми другими фильтрами, которые работают с аутентификацией(по сути этот фильтр должен быть на вершине стеков Spring security).
    LogoutFilterиз документации можно узнать, что этот фильтр проверяет, не направился ли человек на выход из сайта. Он по умолчанию слушает урл /j_spring_security_logout, но это можно изменить в конфигурации. К сожалению, указать, что этот фильтр должен срабатывать только на POST запрос в конфигурации нет возможности, поэтому скорее всего этот фильтр прийдется переоределить. Так же в этом фильтре есть список обработчиков, которые должны быть вызваны в случае, если происходит логаут. И еще в этот список по дефолту попадает обработчик, который инвалидирует сессию:
org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
Так же есть свойство logoutSuccessHandler, которое указывает на обработчик, который должен выполнится после всех хандлеров.
    UsernamePasswordAuthenticationFilter - этот фильтр создается, если тег "form-login" не пустой или выставлена опция "autoConfig".  Обо всем происходящем можно узнать из AuthenticationConfigBuilder.createFormLoginFilter().  В нашем же примере по дефолтной конфигурации создается фильтр, который слушает /j_spring_security_check и на основании параметров запроса j_username и j_password создает UsernamePasswordAuthenticationToken и дальше пробует аутентификовать пользователя любым из доступных провайдеров. Это происходит так: получается ссылка на устанновленый AuthenticationManager и внутри него делается попытка аутентификовать любым из возможных провайдеров аутентификации. В результате аутентицикации мы получаем объект типа Authentication. Если объект не получен(возвращен null), то метод завершается, ничего не делая и не продолжая цепочку вызовов. Однако есть куча обработчиков ошибок, в частности InternalAuthenticationServiceException и AuthenticationException, появление которых свидетульствует об проблемах аутентицикации и запускаются соответственные механизмы.
    Упомянутый выше AuthenticationManager создается в HttpSecurityBeanDefinitionParser.createAuthenticationManager и инициализируется бином org.springframework.security.authentication.ProviderManager, который наполняется свойствами из другого бина, который установлен как свойство  "authentication-manager-refв корневым теге <html>. Так же там создается  пока что пустой список провайдеров аутентификации.
    Так вот. Если это свойство не установлено(а в нашем случае оно не установлено), то создается дефолтный менеджер. Хуйня там какая-то происходит если честно(надеюсь дети не дочитали до этого места). Кароче, в дефолтной конфигурации нашему ProviderManager-у в качестве дополнительного аргумента конструктора передается ссылка на экземпляр AuthenticationManagerFactoryBean, который будет установлен как свойство parent у него(ProviderManager). 
   Единственная задача у  
AuthenticationManagerFactoryBean - получение ссылки на AuthenticationManager  из фабрики, т.е. по сути ищется бин, созданный на основании тега <security:authentication-manager/> из корневого пространства имен security. Т.е. бин типа ProviderManager создается всегда, он инициализируется списком аутентификаторов и еще в качестве свойства "parent" ему указывается бин, который получает ссылку на бин, созданный с помощью тега <authentication-manager>. Все предельно просто. Так же в ProviderManager есть свойство eraseCredentialsAfterAuthentication, которое отвечает за то, чтобы удалить все пароли из токена/пользователя в случае успешной авторизации. Дефолтное значение - true. И вот это свойство тоже копируется из родительского бина. (что интерестно - значение получают с помощью MethodInvokingFactoryBean, т.е. с помощью рефлексии).
    Тут выше было сказано, что создается объект типа ProviderManager и ему с качестве свойства parent указывается реальный AuthenticationManager. (Кстати, ProviderManager есть имплементацией  AuthenticationManager, что и логично, если его можно установить как parentНо какова его задача? Суть в том, что если этот менеджер не смог аутентификировать с помщью хоть одного их своих аутентификаторов, то эта задача передается родителю.
    Теперь об аутентификаторах. Пустой список создается еще в самом начале цепочки, во время парсинга корневого тега <http>(конкретно в методе HttpSecurityBeanDefinitionParser.createFilterChain). В все билдеры потом просто передается ссылка на эту коллекцию. Сама коллекция уже наполняется после создания BeanDefinition для AuthenticationManager. Наполнением этого списка занимается класс org.springframework.security.config.http.AuthenticationConfigBuilder (этот же класс занимается созданием всех основных фильтров, например упомянутый выше LogoutFilter  тоже создается тут). Так вот. Аутентификаторы. Изначально этот метод может создать такие аутентификаторы(если их создание сконфигурировано явно или по умолчанию): 
  • anonymousProviderRef - отвечает за аутентификацию с помощью AnonymousAuthenticationProvider(который просто проверяет если токен типа AnonymousAuthenticationToken и имеет тот же ключ(т.е. созданный именно этим провайдером)) В случае успешной аутентификации этот токен возвращается. (Больше здесь: http://docs.spring.io/autorepo/docs/spring-security/3.1.x/reference/anonymous.html). И все это дело обрабатывается в AnonymousAuthenticationFilter(который кстати тоже присутствует в нашей конфигрурации, потому поговорим о нем позже). Включен по дефолту.
  • rememberMeProviderRef - отвечает за аутентификацию с помощью RememberMeAuthenticationProvider(который просто проверяет если токен типа RememberMeAuthenticationToken и имеет тот же ключ(т.е. созданный именно этим провайдером)).  В случае успешной аутентификации этот токен возвращается. Как мы видим, разница между этим провайдером и анонимным неебольшая(почти). В чем же тогда разница? Если проследить путь создания этого самого токена, то можно увидеть, что он создается в AbstractRememberMeServices, который вызывается из RememberMeAuthenticationFilter. Из этого можно предположить, что точкой входа для всех аутентификаторов являются фильтры. Отключен по дефолту.
  • openIDProviderRef - опциональный аутентификатор, для работы с которым к тому же нужно подключать зависимость. Это наверное стоит отдельного размышления. Как-то связано с OpenID4Java. Не хочу даже сейчас вникать. Одно ясно - при конфигурировании этот сервис аутентификации будет создан.
  • x509ProviderRef - это еще один аутентификатор. Понятия не имею как он работает. Пользователи устанавливают сертификаты, сервер валидирует - ЖУТЬ! хорошо что отключен по дефолту.
  • jeeProviderRef - а вот это уже что-то попрактичней, но очень завязано на j2ee контейнере и его стандартах. Т.е. UserPrincipal должен быть установлен в реквесте. Эта схема наверное будет работать с HTTP basic authentication сервлет контейнера и другими видами авторизации, которые представленны сервлет-контейнером(в соответствии с стандартом j2ee). Однозначно годно. Нужно настраивать. По дефолту отключено.
Вот и все аутентификаторы, которые могут быть созданы конфигуратором. А как же логин по логину и паролю? О них не вспоминалось. И я хз.  Экспериментальным путем(получение authenticationManager по алиасу и вывод всех его аутентификаторов) было установлено, что в дефолтной конфигурации есть только один Аутентификейшн провайдер: DaoAuthenticationProvider. Поиск по коду показал, что инстанс этого класса создается только раз в AuthenticationProviderBeanDefinitionParser, который, как мы знаем, парсит корневой тег "authentication-provider", кстати инстанс которого вытягивает AuthenticationManagerFactoryBean на этапе создания.  Вот и ответ - откуда взялся наш аутентификатор провайдер. Т.е. наш автентификатор в начально конфигурации всегда имеет тип DaoAuthenticationProvider. Там же в AuthenticationProviderBeanDefinitionParser нашему провайдеру устанавливается поле userDetailsService, которое указывает на актуальный юзер-сервис, а так же копируется значение переменной userCache. В этом же классе и происходит loadUserByUsername.

    Итак, UsernamePasswordAuthenticationFilter проверяет урл на паттерн запроса на авторизацию. Если это он, то из запроса вытягиваются логин и пароль, создается UsernamePasswordAuthenticationToken, который потом отдается на сьедение аутентификаторам(и его обработает скорее всего DaoAuthenticationProvider). С этим фильтром закончили. Вот. Потому пока пойдем дальше.
    DefaultLoginPageGeneratingFilter - создается все там же(AuthenticationConfigBuilder.createLoginPageFilterIfNeeded()). Создается, если не страница для логина не определена, но нужна. Т.е. страница логина создается фильтром. Слушает адрес "/spring_security_login". Так как мы не определили собственную страницу для логина, была создана эта.
    BasicAuthenticationFilter - этот фильтр создан, т.к. у нас стоит auto-config='true', что равносильно <http-basic />, этот фильтр отвечает за аутентификацию по BasicHttp Auth, т.е. он из заголовков вытягивает логин и пароль и использует AuthenticationManager для дальнейшей аутентификации. В случае успеха сохраняет аутентицикация в  SecurityContextHolder и rememberMeServices. Так же явно определяется - нужно ли заново аутентифицировать(BasicAuthenticationFilter.authenticationIsRequired(String username)).
    RequestCacheAwareFilter - фильтр, который создается в HttpConfigurationBuilder.createRequestCacheFilter если не задан явно, то все равно создается. Фильтр создается всегда. Он проверяет, соответствует ли входящий запрос запросу из кэша. Если да, то используется запрос из кэша. У самого кэша есть две дефолтные имплементации: NullRequestCache и HttpSessionRequestCache, выбор между которыми зависит от переменной sessionPolicy типа SessionCreationPolicy. Из документации
This allows the request to be restored after the use has authenticated.
Т.е. этот фильтр отвечает за сохранение объекта реквеста в случае, если один из фильтров изменил запрос к изначальному ресурсу через перенаправление на аутентицикацию. Думаю что после аутентицикации этот запрос будет восстановлен и пользователя перенаправят дальше туда куда он шел. Экспериментальным путем было установлено, что в моей простой конфигурации кэш имеет тип HttpSessionRequestCache. Кстати, сам фильтр не является бином, но его все равно можно получить из SecurityFilterChain, так как это его холдер(как и любых других фильтров SS).
    SecurityContextHolderAwareRequestFilter - фильтр, который создается в HttpConfigurationBuilder.createServletApiFilter() по умолчанию(если не сказано обратное), и единственная задача которого - создать врапер вокруг объекта запроса, в первую очередь, чтобы переопределить метод "javax.servlet.http.HttpServletRequest.getUserPrincipal()", ну и добавить остальной функционал. Подробней в SecurityContextHolderAwareRequestWrapper.
    AnonymousAuthenticationFilter - фильтр, который создается в AuthenticationConfigBuilder.createAnonymousFilter по умолчанию, если не сказано обратное. Логика фильтра такова - если пользователь все еще не аутентифицировался, то этот фильтр аутентицицирует его как анонимного пользователя. Так же вместе с фильтром создается аутентификейшн провайдер - AnonymousAuthenticationProvider.  Этот провайдер создается в AuthenticationManager-е, который является чайлдом нашего пользовательского AuthenticationManager-а, этот чайлд создается для каждого тега <http>.  Потому и наш глобальный AuthenticationManager не содержит этого провайдера. Но бин AnonymousAuthenticationProvider есть в контексте. Как получить для данной цепочки фильтров текущего тега <http> актуальный и реальный AuthenticationManager я еще не нашел, как и не нашел места его создания, но то что он есть я узнал из ответа одного самого Люка Тейлора на свой вопрос. Наличие этого бина в контексте я смог благодаря средствам самого спринга(applicationContext.getBeansOfType), пока еще другого способа не нашел.  Так же в исходнике  самого фильтра нет обращений к этому сервису и установку токена в контекст занимается сам фильтр. Я так и не смог найти другого  места, с которого могло бы быть обращение к этому сервису. Зачем этот сервис существует я тоже узнал из того же ответа. Присвоение же всех ролей и имени принципала для анонимного пользователя из тега происходит в фильтр напрямую. 
    SessionManagementFilter - этот фильтр создается по умолчанию(у нас он присутствует) в HttpConfigurationBuilder.createSessionManagementFilters() и конфигурируется внутри тега <http> отдельным тегом <session-management>.
У этого фильтра есть несколько задач: 
  • защита от кражи сессии (http://en.wikipedia.org/wiki/Session_fixation) да, еще никто не отменял этот трюк.
  • в случае, если сессия не валидная(expired) запускается обработчик, который в нашем случае(по умолчанию)  не запускается, так как не установлен. 
  • sessionAuthenticationStrategy решает, что делать с текущей сессией, если авторизация произошла на этом запросе. Дефолтная имплементация - SessionFixationProtectionStrategy - в случае, если авторизация произошла во время этого запросащито?, то эта имплементация создает новую сессию, копируя из старой все объекты. Это сделано для предотвращения session fixation. 
    • Еще в наборе есть её наследник - ConcurrentSessionControlStrategy - который ведет контроль количества одновременных сессий(-1 == ) для принципала с помощью sessionRegistry и так дальше (почитать можно тут: org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy), но нас это не так интересует, так как это не наше дефолтное поведение.
      • и раз мы с этим разобрались, попробуем вывести список онлайн пользователей
        • работает. забавно. почему могло не работать:
          • неверно сформирован аутентификационный запрос. в следствии чего контроль сессий не вызывался изначально из фильтра. пожалуй это единственная и очевидная проблема. но мы уже не знаем точно.
      • дефолтная имплементация хранилища сессий своей логикой не особо отличается он моей пользовательской имплементации - org.springframework.security.core.session.SessionRegistryImpl и для его работы так же нужно зарегистрировать в листнер в web.xml: 
        <listener>
             <listener-class>
                  org.springframework.security.web.session.HttpSessionEventPublisher
             </listener-class>
        </listener>
        Т.е. разница не принципиальна.
    • (откуда фильтра знает, что это первый запрос с авторизацией?)так же важной задачей этого фильтра является 
  • в случае превышения количества возможных сессий(количество возможных сессий?) будет запущен обработчик failureHandler , который по умолчанию заставляет сервер вернуть 401 еррор код(т.е. в нашем случае, а если еще и установить defaultFailureUrl, то будет переходить на него, при этом детальная информация об ошибке сохраняется)
  • так же этот фильтр после первого входа и обновления секюрити контекста наново сохраняет его. Но откуда фильтр знает, что это первый вход?  Все просто - сначала идет проверка - если секюрити контекст не сохранен в текущей сессии, но он уже существует в SecurityContextHolder.getContext().getAuthentication(), значит это первый запрос в текущей автентификации. После выполнения всех возможных обработчиков контекст сохраняется в сессии (что гарантирует, что он больше не будет вызван) 
    ExceptionTranslationFilter - этот фильтр создается в AuthenticationConfigBuilder.createExceptionTranslationFilter(), создается всегда. об его задачах полностью написано в самом фильтре.   Если коротко - если во время запроса возникнет AuthenticationException, то этот фильтр решает что делать - для незалогиненого пользователя вызывает authenticationEntryPoint. Гм.. еще я не писал об этом, ну ок. Так вот. Для каждого <http> тега внутри AuthenticationConfigBuilder  создается некая "entry-point", которая отвечает за "точку входа", т.е. за способ входа в приложение(очень частный, но самым популярны вариант - это форма логина(LoginUrlAuthenticationEntryPoint), другой известный вариант - запрос BASICHTTP авторизации(BasicAuthenticationEntryPoint)). По умолчанию(наш случай) создается именно LoginUrlAuthenticationEntryPoint. Так вот. Этот наш фильтр вызывает эту "entry-point", сохранив текущий запрос в кэш(откуда запрос будет восстановлен в RequestCacheAwareFilter  посте того, как пройдет аутентификация и авторизация(смотрим на порядок фильтров - все фильтры что выполняют эту операцию стоят ПЕРЕД фильтром, который восстанавливает запрос из кэша)).
    FilterSecurityInterceptor - создается в HttpConfigurationBuilder.createFilterSecurityInterceptor и отвечает за авторизацию. Именно за авторизацию - проверяет, есть ли у текущего пользователя права на доступ к ресурсу. Основная работа выполняется в предке этого фильтра - AbstractSecurityInterceptor, а уже этот класс применяет его логику в виде фильтра. Есть и другие наследники AbstractSecurityInterceptorMethodSecurityInterceptor. Для проверки наличия прав используются разные AccessDecisionManager, который проверяет доступ к ресурсу с помощью войтеров. Есть несколько разных возможных AccessDecisionManager-ов, в нашем простейшем примере автоматически был создан AffirmativeBased(он разрешает доступ к ресурсу, если хотя бы один войтер разрешил доступ), но кроме него есть еще ConsensusBased(войтеры голосуют за и против и побеждает то решение, которое наберет больше голосов) и UnanimousBased(требует, чтобы все войтеры разрешили доступ).  По умолчанию создается два войтера - RoleVoter и AuthenticatedVoter, но это поведение можно легко изменить, добавив атрибут "use-expressions" к тегу <html>, установив его равным "true". Тогда будет создаваться новый войтер - WebExpressionVoter, который будет проверять права на доступ на основании експрешинов. Подробней тут: http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html.

Примеры реализации SS модели без нейспейс-конфигуратора - первый попавшийся из гугла движок для блогов: 

Features: 


Еще об провайдер менеджерах - корневой менеджер создается с тега в конфигурации, однако для каждого тега <http> создается его наследник. 


Вот и с стеком все.
Теперь будем изменять.

    
    


    

    













Дальше обратимся на корневую сайта и посмотрим логи:
DEBUG (FilterChainProxy.java:337) / at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG (HttpSessionSecurityContextRepository.java:127) No HttpSession currently exists
DEBUG (HttpSessionSecurityContextRepository.java:85) No SecurityContext was available from the HttpSession: null. A new one will be created.
DEBUG (FilterChainProxy.java:337) / at position 2 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG (FilterChainProxy.java:337) / at position 3 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG (FilterChainProxy.java:337) / at position 4 of 11 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
DEBUG (FilterChainProxy.java:337) / at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
DEBUG (FilterChainProxy.java:337) / at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG (FilterChainProxy.java:337) / at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG (FilterChainProxy.java:337) / at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG (AnonymousAuthenticationFilter.java:102) Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
DEBUG (FilterChainProxy.java:337) / at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
DEBUG (SessionManagementFilter.java:92) Requested session ID 9wc3lqcwl6jf1jx3k1ra47msl is invalid.
DEBUG (FilterChainProxy.java:337) / at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
DEBUG (FilterChainProxy.java:337) / at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG (AntPathRequestMatcher.java:116) Checking match of request : '/'; against '/secure/**'
DEBUG (AntPathRequestMatcher.java:116) Checking match of request : '/'; against '/admin/**'
DEBUG (AbstractSecurityInterceptor.java:185) Public object - authentication not attempted
DEBUG (FilterChainProxy.java:323) / reached end of additional filter chain; proceeding with original chain
DEBUG (HttpSessionSecurityContextRepository.java:269) SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
DEBUG (ExceptionTranslationFilter.java:115) Chain processed normally
DEBUG (SecurityContextPersistenceFilter.java:97) SecurityContextHolder now cleared, as request processing completed

Обо всем по порядке.



Добавим зависимости в pom.xml:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${org.springframework-version}</version> </dependency>


Добавить в главный контекст приложения:
<import resource="security.xml"/>

ну и создать этот файл:
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <http access-denied-page="/error403.jsp">
        <intercept-url pattern="/index*" access="ROLE_USER,ROLE_ANONYMOUS" />
        <intercept-url pattern="/add*" access="ROLE_USER" />
        <intercept-url pattern="/delete/*" access="ROLE_ADMIN" />
        <form-login login-page="/login.jsp" default-target-url="/index"
            authentication-failure-url="/login.jsp?error=true" />
        <logout logout-url="/logout" logout-success-url="/index" />
        
        <anonymous username="guest" granted-authority="ROLE_ANONYMOUS" />
        <remember-me />
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="pass" authorities="ROLE_ADMIN,ROLE_USER" />
                <user name="user1" password="1111" authorities="ROLE_USER" />
                <user name="user2" password="2222" disabled="true" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

и добавить фильтр в web.xml:
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Использование:
<c:if test="${not empty param.error}">
    <font color="red"> label.loginerror
    : ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message} </font>
</c:if>
<form method="POST" action="<c:url value="/j_spring_security_check" />">
<p>login: <input type="text" name="j_username" /></p>
<p>password: <input type="password" name="j_password" /></p>
<p>remember: <input type="checkbox" name="_spring_security_remember_me" /></p>
<p><input type="submit" value="Login" /><input type="reset" value="Reset" /></p>
</form>

Другие интересные ключи:
<remember-me /> - авторизация не умирает с сесией
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS" /> - работаем с незалогинеными пользователями
 

Теперь прикрутим базу к пользователям.
http://ru.wikipedia.org/wiki/Spring_Security
http://ru.wikibooks.org/wiki/Spring_Security
http://www.mkyong.com/tutorials/spring-security-tutorials/
http://www.seostella.com/ru/article/2012/06/27/vvedenie-v-spring-security-hello-world.html
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/appendix-schema.html
http://heraclitusonsoftware.wordpress.com/software-development/spring/simple-web-application-with-spring-security-part-10/
http://packtlib.packtpub.com/library/9781847199744/ch04lvl1sec22




Comments