Использование Spring Security с RESTful API

RESTful

представлять

В этой статье мы узнаем, как обеспечить более безопасный RESTful API с помощью Spring и Spring Security 5.
Мы будем использовать конфигурацию Java для настройки безопасности и будем использовать методы входа и cookie для аутентификации.

Включить весеннюю безопасность

Архитектура Spring Security полностью основана на фильтрах сервлетов.
Самый простой способ зарегистрировать фильтры Spring Security — добавить аннотацию @EnableWebSecurity в наш класс конфигурации:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
 
    // ...
}

Для приложений, отличных от Spring Boot, мы можем расширить AbstractSecurityWebApplicationInitializer и передать класс конфигурации в его конструктор:

public class SecurityWebApplicationInitializer 
  extends AbstractSecurityWebApplicationInitializer {
 
    public SecurityWebApplicationInitializer() {
        super(SecurityJavaConfig.class);
    }
}

Или мы можем объявить это в файле web.xml приложения:

<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», чтобы он соответствовал bean-компоненту по умолчанию, созданному Spring Security в контейнере.
Обратите внимание, что определенный фильтр не является фактическим классом, реализующим логику безопасности. Вместо этого это делегирующий filterproxy, который делегирует методы фильтра внутреннему компоненту. Это сделано для того, чтобы целевой компонент по-прежнему мог извлекать выгоду из жизненного цикла и гибкости контекста Spring.

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

Мы можем выполнить настройку безопасности полностью в классе 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();
}

Http

В нашей реализации кода мы используем antMatchers для создания безопасных сопоставлений /api/foos и /api/admin/**.
Любой аутентифицированный пользователь может получить доступ к /api/foos. С другой стороны, /api/admin/** могут получить доступ только пользователи с ролью администратора.
В стандартном веб-приложении процесс проверки подлинности может запускаться автоматически, когда клиент, не прошедший проверку подлинности, пытается получить доступ к защищенному ресурсу. Этот процесс обычно перенаправляет на страницу входа, чтобы пользователь мог ввести учетные данные.
Однако для веб-служб REST такое поведение не имеет особого смысла.Мы должны иметь возможность аутентифицировать только запросы к правильному URI, если пользователь не аутентифицирован, все запросы должны завершаться с ошибкой с кодом состояния 401 Unauthorized.
Spring Security использует концепцию точки входа для автоматического запуска процесса аутентификации.— Это обязательная часть конфигурации, которую можно внедрить с помощью метода authenticationEntryPoint.
Просто верните 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");
    }
}

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

Существует несколько способов аутентификации REST API. Одним из значений по умолчанию, предоставляемых Spring Security, является форма входа в систему, в которой используется фильтр обработки аутентификации org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.
Элемент formLogin создаст этот фильтр и предоставит дополнительные методы successHandler и failureHandler для установки наших пользовательских обработчиков успешной и неудачной аутентификации соответственно.

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

По умолчанию форма входа будет отвечать на успешные запросы аутентификации кодом состояния 301, что имеет смысл в контексте фактической формы входа, которую необходимо перенаправить после входа.
Однако,Для веб-служб RESTful требуемый ответ для успешной аутентификации должен быть 200 OK.
с этой целью,Мы внедряем собственный обработчик успешной аутентификации в фильтр входа в форму., чтобы заменить обработчик по умолчанию. Новый обработчик реализует точно такой же вход в систему, как и стандартный org.springframe.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;
    }
}

Менеджеры и поставщики аутентификации

Процесс проверки подлинности использует поставщика в памяти для выполнения проверки подлинности.
Мы создали двух пользователей, пользователя с ролью USER и администратора с ролью ADMIN.

Наконец - Аутентификация против работающей службы 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 для аутентификации и cookie, который он получает.сохранить в файл:

curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt 
http://localhost:8080/spring-security-rest/login

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

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

Поскольку у пользователя есть доступ к конечным точкам /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":"qulingfeng"}]

Точно так же для пользователя-администратора мы можем использовать 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

Поскольку пользователь-администратор имеет доступ к конечным точкам /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

XML-конфигурация

Мы также можем использовать XML вместо конфигурации Java для выполнения всех вышеперечисленных настроек безопасности:

<http entry-point-ref="restAuthenticationEntryPoint">
    <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/>
 
    <form-login
      authentication-success-handler-ref="mySuccessHandler"
      authentication-failure-handler-ref="myFailureHandler" />
 
    <logout />
</http>
 
<beans:bean id="mySuccessHandler"
  class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
<beans:bean id="myFailureHandler" class=
  "org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>
 
<authentication-manager alias="authenticationManager">
    <authentication-provider>
        <user-service>
            <user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
            <user name="user" password="userPass" authorities="ROLE_USER"/>
        </user-service>
    </authentication-provider>
</authentication-manager>

заключительные замечания

В этом руководстве мы рассмотрели базовую настройку безопасности и реализацию для служб RESTful с использованием Spring security 5.
Мы узнали, как настроить безопасность для REST API полностью с помощью конфигурации Java, и рассмотрели его альтернативы конфигурации web.xml.
Далее мы обсудили, как создавать пользователей и роли для защищенных приложений и как сопоставлять этих пользователей с конкретными конечными точками приложения.
Наконец, мы также рассмотрели, как создавать настраиваемые точки входа для аутентификации и настраиваемые обработчики успеха, чтобы предоставить приложениям большую гибкость в управлении безопасностью.

Добро пожаловать, чтобы обратить внимание на мой общедоступный аккаунт: Цюй Линфэн, чтобы получить эксклюзивные учебные ресурсы и ежедневный толчок галантерейных товаров.
Если вас интересует мой рекомендуемый контент, вы также можете подписаться на мой блог:sagowiec.com