Статьи

Защита веб-службы RESTful с помощью Spring Security 3.1, часть 3

1. Обзор

В этом руководстве показано, как защитить службу REST с помощью Spring и Spring Security 3.1 с настройкой на основе Java. Статья будет посвящена настройке Конфигурации безопасности специально для REST API с использованием подхода Login и Cookie.

2. Spring Security в web.xml

Архитектура Spring Security полностью основана на фильтрах сервлетов и, как таковая, предшествует Spring MVC в отношении обработки HTTP-запросов. Имея это в виду, для начала необходимо объявить фильтр в web.xml приложения:

1
2
3
4
5
6
7
8
<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>

Фильтр должен обязательно называться «springSecurityFilterChain», чтобы соответствовать компоненту по умолчанию, созданному Spring Security в контейнере.

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

Шаблон URL, используемый для настройки фильтра, — это / *, даже если вся веб-служба сопоставлена ​​с / api / *, поэтому в конфигурации безопасности есть возможность защитить и другие возможные сопоставления, если это необходимо.

3. Конфигурация безопасности

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
   xsi:schemaLocation="
 
   <http entry-point-ref="restAuthenticationEntryPoint">
      <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/>
 
      <form-login authentication-success-handler-ref="mySuccessHandler" />
 
      <logout />
   </http>
 
   <beans:bean id="mySuccessHandler"
    class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
 
   <authentication-manager alias="authenticationManager">
      <authentication-provider>
         <user-service>
            <user name="temporary" password="temporary" authorities="ROLE_ADMIN"/>
            <user name="user" password="user" authorities="ROLE_USER"/>
         </user-service>
      </authentication-provider>
   </authentication-manager>
 
</beans:beans>

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

3.1. Элемент <http>

Элемент <http> является основным элементом контейнера для настройки безопасности HTTP. В текущей реализации он защищал только одно отображение: / api / admin / ** . Обратите внимание, что сопоставление относится к корневому контексту веб-приложения, а не к остальному сервлету; это связано с тем, что вся конфигурация безопасности находится в корневом контексте Spring, а не в дочернем контексте сервлета.

3.2. Точка входа

В стандартном веб-приложении процесс аутентификации может автоматически запускаться, когда клиент пытается получить доступ к защищенному ресурсу без аутентификации — обычно это происходит путем перенаправления на страницу входа, чтобы пользователь мог ввести учетные данные. Однако для веб-службы REST это поведение не имеет особого смысла — проверка подлинности должна выполняться только путем запроса правильного URI, а все остальные запросы должны просто завершаться неудачей с кодом состояния 401 НЕСАНКЦИОНИРОВАННО, если пользователь не прошел проверку подлинности.

Spring Security обрабатывает этот автоматический запуск процесса аутентификации с помощью концепции точки входа — это обязательная часть конфигурации, и ее можно внедрить с помощью атрибута entry-point-ref элемента <http> . Учитывая, что эта функциональность не имеет смысла в контексте службы REST, новая настраиваемая точка входа определяется так, чтобы просто возвращать 401 при каждом ее срабатывании:

1
2
3
4
5
6
7
8
9
@Component( "restAuthenticationEntryPoint" )
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
 
   @Override
   public void commence( HttpServletRequest request, HttpServletResponse response,
    AuthenticationException authException ) throws IOException{
      response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

3.3. Форма входа для REST

Существует несколько способов выполнить проверку подлинности для API REST — одним из стандартных решений, предлагаемых Spring Security, является вход в систему формы — который использует фильтр обработки проверки подлинности — org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter .

Элемент <form-login> создаст этот фильтр, а также позволит нам настроить наш собственный обработчик успеха аутентификации. Это также можно сделать вручную, используя элемент <custom-filter> для регистрации фильтра в позиции FORM_LOGIN_FILTER — но поддержка пространства имен достаточно гибкая.

Обратите внимание, что для стандартного веб-приложения атрибут auto-config элемента <http> является сокращенным синтаксисом для некоторой полезной конфигурации безопасности. Хотя это может быть подходящим для некоторых очень простых конфигураций, оно не подходит и не должно использоваться для REST API.

3.4. Аутентификация должна возвращать 200 вместо 301

По умолчанию форма входа в систему ответит на успешный запрос аутентификации с кодом состояния 301 MOVED PERMANENTLY ; это имеет смысл в контексте реальной формы входа в систему, которую необходимо перенаправить после входа в систему. Однако для веб-службы RESTful желаемый ответ для успешной аутентификации должен быть 200 OK .

Это делается путем добавления пользовательского обработчика успешной аутентификации в фильтр входа в систему, чтобы заменить стандартный. Новый обработчик реализует точно такой же логин, как по умолчанию org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler, с одним заметным отличием — логика перенаправления удалена:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MySavedRequestAwareAuthenticationSuccessHandler
      extends SimpleUrlAuthenticationSuccessHandler {
 
    private RequestCache requestCache = new HttpSessionRequestCache();
 
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
      Authentication authentication) throws ServletException, IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);
 
        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }
        String targetUrlParam = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl() ||
          (targetUrlParam != null &&
          StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }
 
        clearAuthenticationAttributes(request);
    }
 
    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

3.5. Диспетчер аутентификации и провайдер

Процесс аутентификации использует провайдера в памяти для выполнения аутентификации — это предназначено для упрощения конфигурации, поскольку производственная реализация этих артефактов выходит за рамки этого поста.

3.6 Наконец — Аутентификация на работающей службе REST

Теперь давайте посмотрим, как мы можем аутентифицироваться по REST API — URL для входа в систему — / j_spring_security_check — и простая команда curl, выполняющая вход в систему, будет выглядеть так:

1
2
curl -i -X POST -d j_username=user -d j_password=userPass
http://localhost:8080/spring-security-rest/j_spring_security_check

Этот запрос вернет Cookie, который затем будет использоваться любым последующим запросом к службе REST.

Мы можем использовать curl для аутентификации и сохранить полученный файл cookie в файле :

1
2
curl -i -X POST -d j_username=user -d j_password=userPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/j_spring_security_check

Затем мы можем использовать cookie из файла для выполнения дальнейших аутентифицированных запросов:

1
2
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/foos

Этот аутентифицированный запрос корректно приведет к 200 OK :

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 24 Jul 2013 20:31:13 GMT
 
[{"id":0,"name":"JbidXc"}]

4. Maven и другие неприятности

Основные зависимости Spring, необходимые для веб-приложения и службы REST, подробно обсуждались. В целях безопасности нам нужно добавить: spring-security-web и spring-security-config — все они также были описаны в учебнике по Maven для Spring Security .

Стоит обратить пристальное внимание на то, как Maven будет разрешать старые зависимости Spring — стратегия разрешения начнет вызывать проблемы, как только артефакты безопасности будут добавлены в pom. Чтобы решить эту проблему, некоторые из основных зависимостей необходимо будет переопределить, чтобы сохранить их в правильной версии.

5. Заключение

В этом посте рассказывалось о базовой настройке и реализации безопасности для службы RESTful с использованием Spring Security 3.1 , обсуждались web.xml , конфигурация безопасности, коды состояния HTTP для процесса аутентификации и разрешение Maven артефактов безопасности.

Реализацию этого Spring REST Tutorial можно найти в проекте github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.

Ссылка: Spring REST Service Security 3 от нашего партнера JCG Евгения Параскива в блоге baeldung .