Spring Security для REST API
1. обзор
В этом руководстве мы узнаем, какSecure a REST API using Spring and Spring Security 5.
Мы установим безопасность, используя конфигурацию Java, и будем использовать подход «Логин» и «Cookie» для аутентификации.
Дальнейшее чтение:
Учебное пособие по аутентификации Spring
Как создать производственный процесс регистрации для новых пользователей и поток входа в систему для существующих пользователей.
Предоставленная власть против роли в весенней безопасности
Краткое руководство по разнице между предоставленными полномочиями и ролью в Spring Security.
Spring Security Basic Authentication
Настроить базовую аутентификацию в Spring - Конфигурация XML, Сообщения об ошибках и пример использования защищенных URL-адресов с помощью curl.
2. Включить Spring Security
The architecture of Spring Security is based entirely on Servlet Filters. Следовательно, это относится к Spring MVC в отношении обработки HTTP-запросов.
Самый простой способ зарегистрировать фильтр безопасности Spring - это аннотировать наш класс конфигурации с помощью@EnableWebSecurity:
@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
// ...
}
Для приложения, отличного от Spring Boot, мы можем расширитьAbstractSecurityWebApplicationInitializer и передать наш класс конфигурации в его конструктор:
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityJavaConfig.class);
}
}
Или мы можем объявить это вweb.xml приложения:
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
Мы должны назвать фильтр‘springSecurityFilterChain', чтобы он соответствовал bean-компоненту по умолчанию, созданному Spring Security в контейнере.
Обратите внимание, что определенный фильтр не является фактическим классом, реализующим логику безопасности. Вместо этогоDelegatingFilterProxy делегирует методы фильтра внутреннему компоненту. Это сделано для того, чтобы целевой компонент мог по-прежнему пользоваться жизненным циклом Spring и гибкостью.
Шаблон URL, используемый для настройки фильтра, -/*, даже если вся веб-служба сопоставлена с/api/*. Это дает конфигурации безопасности возможность защищать и другие возможные сопоставления, если это необходимо.
3. Конфигурация Java Spring Security
Мы можем полностью настроить безопасность в классе Java, создав класс конфигурации, который расширяетWebSecurityConfigurerAdapter и аннотируя его с помощью@EnableWebSecurity:
@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
// ...
}
Теперь давайте создадим пользователей с разными ролями вSecurityJavaConfig, которые мы будем использовать для аутентификации наших конечных точек API:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
.and()
.withUser("user").password(encoder().encode("userPass")).roles("USER");
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
Затем давайте настроим безопасность для наших конечных точек API:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/foos").authenticated()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.successHandler(mySuccessHandler)
.failureHandler(myFailureHandler)
.and()
.logout();
}
3.1. Элементhttp
Элементhttp является стартовым элементом для настройки безопасности и предоставляет нам быстрые и гибкие методы настройки безопасности.
В нашей реализации мы создаем защищенные сопоставления/api/foos and/api/admin/** usingantMatchers.
Шаблон/api/foos доступен любому аутентифицированному пользователю. С другой стороны,/api/admin/** w будет доступен только для пользователей ролиADMIN .
3.2. Точка входа
В стандартном веб-приложении процесс аутентификации может автоматически запускаться, когда неаутентифицированный клиент пытается получить доступ к защищенному ресурсу. Этот процесс обычно перенаправляет на страницу входа, чтобы пользователь мог ввести учетные данные.
Однако для веб-службы REST такое поведение не имеет особого смысла. We should be able to authenticate only by a request to the correct URI and if the user is not authenticated all requests should simply fail with a 401 UNAUTHORIZED status code.с
Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point - это обязательная часть конфигурации, которая может быть введена с помощью методаauthenticationEntryPoint .
Учитывая, что эта функциональность не имеет смысла в контексте службы REST, мы определяем новую пользовательскую точку входа, чтобы просто возвращать 401 при запуске:
@Component
public final class RestAuthenticationEntryPoint
implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized");
}
}
Небольшое примечание: 401 отправляется без заголовкаWWW-Authenticate, как того требует спецификация HTTP. Мы можем, конечно, установить значение вручную, если нам нужно.
3.3. Форма входа в REST
Есть несколько способов сделать аутентификацию для REST API. Одним из значений по умолчанию, предоставляемых Spring Security, является Form Login, в котором используется фильтр обработки аутентификации -org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.
The formLogin element will create this filter and also provides additional methods successHandler and failureHandler, чтобы установить наши собственные обработчики успешной и неудачной аутентификации соответственно.
Обратите внимание, что для стандартного веб-приложения аннотация@EnableWebSecurity сама настраивает множество конфигураций по умолчанию.
3.4. Аутентификация должна возвращать 200 вместо 301
По умолчанию форма входа в систему ответит на успешный запрос аутентификации кодом состояния301 MOVED PERMANENTLY; это имеет смысл в контексте фактической формы входа в систему, которая должна перенаправляться после входа в систему.
Однакоfor a RESTful web service, the desired response for a successful authentication should be 200 OK.
We do this by injecting a custom authentication success handler в фильтре входа в форму, чтобы заменить фильтр по умолчанию. Новый обработчик реализует тот же логин, что иorg.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler по умолчанию, с одним заметным отличием - он удаляет логику перенаправления:
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. Ошибка аутентификации должна возвращать 401 вместо 302
Аналогично - мы настроили обработчик ошибок аутентификации - так же, как мы сделали с обработчиком успеха.
К счастью, в этом случае нам не нужно фактически определять новый класс для этого обработчика - стандартная реализация -SimpleUrlAuthenticationFailureHandler - отлично подходит.
3.6. Менеджер аутентификации и провайдер
Процесс аутентификации используетin-memory provider для выполнения аутентификации. Это должно упростить настройку, поскольку производственная реализация этих артефактов выходит за рамки данной статьи.
Мы создали двух пользователей, а именноuser роль для бритьяUSER andadmin с рольюADMIN.
3.7. Наконец - аутентификация против работающей службы REST
Теперь давайте посмотрим, как мы можем аутентифицироваться с помощью REST API для разных пользователей.
URL-адрес для входа -/login - и простая командаcurl, выполняющая вход для пользователя с именемuser и паролемuserPass, будет выглядеть так:
curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login
Этот запрос вернет Cookie, который мы можем использовать для любого последующего запроса к службе REST.
Мы можем использоватьcurl для аутентификации иstore the cookie it receives in a file:
curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login
Затемwe can use the cookie from the file для выполнения дальнейших аутентифицированных запросов:
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/foos
Поскольку сканированиеuser обращается к точке отправки/api/foos/ , этот аутентифицированный запрос * правильно * приведет к 200 OK:
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"}]
Точно так же дляadmin user мы можем использоватьcurl для аутентификации:
curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt
http://localhost:8080/spring-security-rest/login
а затем обновленные файлы cookie для доступа к конечным точкам администратора/api/admin/*:
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt
http://localhost:8080/spring-security-rest/api/admin/x
Посколькуadmin user может получить доступ к конечной точке/api/admin/* , это приведет к успешному ответу:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 5
Date: Mon, 15 Oct 2018 17:16:39 GMT
Hello
4. Конфигурация безопасности XML
Мы также можем выполнить все вышеуказанные настройки безопасности, используя XML вместо конфигурации Java:
Большая часть конфигурации выполняется с использованием пространства имен безопасности. Чтобы включить это, нам нужно определить расположение схемы.
Пространство имен спроектировано таким образом, что оно выражает наиболее распространенные варианты использования Spring Security, в то же время предоставляя перехватчики для необработанных компонентов для поддержки более сложных сценариев.
5. Заключение
В этом руководстве мы рассмотрели базовую конфигурацию и реализацию безопасности для службы RESTful с использованием Spring Security 5.
Мы узнали, как настроить безопасность для нашего REST API полностью через конфигурацию Java, а также рассмотрели альтернативу конфигурацииweb.xml.
Далее мы обсудили, как создавать пользователей и роли для нашего защищенного приложения, а также как сопоставить этих пользователей с конкретными конечными точками нашего приложения.
Наконец, мы также рассмотрели создание настраиваемой точки входа для проверки подлинности и настраиваемого обработчика успеха, который дал нашему приложению большую гибкость в плане контроля безопасности.
Доступна полная реализацияover on Github.