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=userPasshttp://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.txthttp://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 OKServer: Apache-Coyote/1.1Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: 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, поэтому его легко импортировать и запускать как есть.