Ответ 1
Я бы предложил установить ваши фильтры безопасности Spring полностью вручную. Это не так сложно, и вы полностью контролируете поведение входа/выхода из системы.
Прежде всего вам понадобится стандартная реклама web.xml для делегирования обработки цепочки фильтров на Spring (удалите async-поддержку, если вы не используете Servlet API ver 3):
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<async-supported>true</async-supported>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Теперь в контексте безопасности вы будете определять фильтры отдельно для каждого пути. Фильтры могут аутентифицировать пользователя, выходить из системы, проверять учетные данные и т.д.
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/login" filters="sif,wsFilter"/>
<sec:filter-chain pattern="/logout" filters="sif,logoutFilter" />
<sec:filter-chain pattern="/rest/**" filters="sif,fsi"/>
</sec:filter-chain-map>
</bean>
В приведенном выше XML-документе говорится, что Spring передает запросы на конкретные контекстно-зависимые URL-адреса через цепи фильтров. Первое, что происходит в любой из цепочек фильтров, заключается в установлении контекста безопасности - "sif" bean позаботится об этом.
<bean id="sif" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
Следующий фильтр в цепочке теперь может либо добавить данные в контекст безопасности (читать: войти/выйти из системы), либо принять решение о разрешении доступа на основе указанного контекста безопасности.
Для вашего URL входа в систему вам понадобится фильтр, который считывает данные аутентификации из запроса, проверяет его и, в свою очередь, сохраняет его в контексте безопасности (который хранится в сеансе):
<bean id="wsFilter" class="my.own.security.AuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="myAuthSuccessHandler"/>
<property name="passwordParameter" value="pass"></property>
<property name="usernameParameter" value="user"></property>
<property name="postOnly" value="false"></property>
Вы можете использовать Spring generic UsernamePasswordAuthenticationFilter
, но причина, по которой я использую свою собственную реализацию, - продолжить обработку цепочки фильтров (реализация по умолчанию предполагает, что пользователь будет перенаправлен на успешную авторизацию и завершает цепочку фильтров) и сможет обрабатывать аутентификацию каждый раз, когда имя пользователя и пароль передаются ему:
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return ( StringUtils.hasText(obtainUsername(request)) && StringUtils.hasText(obtainPassword(request)) );
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException{
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
Вы можете добавить любое количество собственных реализаций фильтра для/пути входа, например, аутентификацию с использованием HTTP-заголовка основного заголовка, заголовка дайджеста или даже извлечения username/pwd из тела запроса. Spring предоставляет для этого множество фильтров.
У меня есть собственный обработчик успеха, который переопределяет стратегию перенаправления по умолчанию:
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@PostConstruct
public void afterPropertiesSet() {
setRedirectStrategy(new NoRedirectStrategy());
}
protected class NoRedirectStrategy implements RedirectStrategy {
@Override
public void sendRedirect(HttpServletRequest request,
HttpServletResponse response, String url) throws IOException {
// no redirect
}
}
}
Вам не обязательно иметь настраиваемый обработчик успешной проверки подлинности (и, возможно, настраиваемый фильтр auth), если вы в порядке с перенаправлением пользователя после успешного входа в систему (URL-адрес перенаправления можно настроить, проверить документы)
Определите менеджера аутентификации, который будет отвечать за получение сведений о пользователе:
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="myAuthAuthProvider"/>
</sec:authentication-manager>
<bean id="myAuthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="myUserDetailsImpl"/>
</bean>
</property>
</bean>
Здесь вы должны будете предоставить свою собственную информацию о пользователе bean.
Фильтр выхода: отвечает за очистку контекста безопасности
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
Общий материал аутентификации:
<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>
Фильтр контроля доступа (должен быть понятным):
<bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="myAuthenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadataSource">
<sec:filter-invocation-definition-source>
<sec:intercept-url pattern="/rest/**" access="ROLE_REST"/>
</sec:filter-invocation-definition-source>
</property>
</bean>
Вы также должны иметь возможность защищать свои службы REST с помощью аннотаций @Secured
на методах.
Контекст выше был вырван из существующей службы веб-сервиса REST - извините за возможные опечатки.
Также возможно выполнить, по крайней мере, большую часть того, что реализовано здесь, используя теги sec
Spring, но я предпочитаю настраиваемый подход, так как это дает мне наибольший контроль.
Надеюсь, это по крайней мере поможет вам начать.