Spring Security для REST API

Spring Security для REST API

1. обзор

В этом руководстве мы узнаем, какSecure a REST API using Spring and Spring Security 5.

Мы установим безопасность, используя конфигурацию Java, и будем использовать подход «Логин» и «Cookie» для аутентификации.

Дальнейшее чтение:

Учебное пособие по аутентификации Spring

Как создать производственный процесс регистрации для новых пользователей и поток входа в систему для существующих пользователей.

Read more

Предоставленная власть против роли в весенней безопасности

Краткое руководство по разнице между предоставленными полномочиями и ролью в Spring Security.

Read more

Spring Security Basic Authentication

Настроить базовую аутентификацию в Spring - Конфигурация XML, Сообщения об ошибках и пример использования защищенных URL-адресов с помощью curl.

Read more

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.