Подтвердить что ты не робот

Вход/выход в REST с помощью Spring 3

Мы разрабатываем веб-службы RESTful с Spring 3, и нам нужно иметь функции входа/выхода из системы, что-то вроде /webservices/login/<username>/<password>/ и /webservices/logout. Сеанс должен храниться в контексте до тех пор, пока сеанс не будет завершен или не выйдет из системы, чтобы разрешить потребление других веб-сервисов. Любой запрос на доступ к веб-службам без информации о сеансе должен быть отклонен. Ищете самое современное решение для этого сценария.

Я действительно воскрешаю вопрос, заданный здесь Spring Security 3 программным способом входа в систему, который по-прежнему не отвечает должным образом. Также укажите изменения, необходимые в web.xml.

4b9b3361

Ответ 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, но я предпочитаю настраиваемый подход, так как это дает мне наибольший контроль.

Надеюсь, это по крайней мере поможет вам начать.