«1. Обзор

В этом кратком руководстве мы рассмотрим, как защитить веб-приложение Jakarta EE с помощью Spring Security.

2. Зависимости Maven

Начнем с необходимых зависимостей Spring Security для этого руководства:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>

Последняя версия Spring Security (на момент написания этого руководства) — 4.2.3.RELEASE; как всегда, мы можем проверить Maven Central на наличие новейших версий.

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

Далее нам нужно настроить конфигурацию безопасности для существующего приложения Jakarta EE:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig 
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("user1").password("user1Pass").roles("USER")
          .and()
          .withUser("admin").password("adminPass").roles("ADMIN");
    }
}

В методе configure() мы настраиваем AuthenticationManager. Для простоты мы реализуем простую аутентификацию в памяти. Данные пользователя жестко закодированы.

Это предназначено для быстрого прототипирования, когда нет необходимости в полном механизме персистентности.

Далее давайте интегрируем безопасность в существующую систему, добавив класс SecurityWebApplicationInitializer:

public class SecurityWebApplicationInitializer
  extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SpringSecurityConfig.class);
    }
}

Этот класс обеспечит загрузку SpringSecurityConfig во время запуска приложения. На этом этапе мы достигли базовой реализации Spring Security. В этой реализации Spring Security будет требовать аутентификацию для всех запросов и маршрутов по умолчанию.

4. Настройка правил безопасности

Мы можем дополнительно настроить Spring Security, переопределив метод configure(HttpSecurity http) WebSecurityConfigurerAdapter: доступ к /auth/login и аутентификация любого другого запроса.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeRequests()
      .antMatchers("/auth/login*").anonymous()
      .anyRequest().authenticated()
      .and()
      .formLogin()
      .loginPage("/auth/login")
      .defaultSuccessUrl("/home", true)
      .failureUrl("/auth/login?error=true")
      .and()
      .logout().logoutSuccessUrl("/auth/login");
}

4.1. Пользовательская страница входа

Пользовательская страница входа настраивается с использованием метода formLogin():

Если это не указано, Spring Security создает страницу входа по умолчанию в /login:

http.formLogin()
  .loginPage("/auth/login")

4.2. Пользовательская целевая страница

<html>
<head></head>
<body>
<h1>Login</h1>
<form name='f' action="/auth/login" method='POST'>
    <table>
        <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type='password' name='password'/></td>
        </tr>
        <tr>
            <td><input name="submit" type="submit" 
              value="submit"/></td>
        </tr>
    </table>
</form>
</body>
</html>

После успешного входа в систему Spring Security перенаправляет пользователя в корень приложения. Мы можем переопределить это, указав URL-адрес успеха по умолчанию:

Установив для параметра alwaysUse метода defaultSuccessUrl() значение true, пользователь всегда будет перенаправляться на указанную страницу.

http.formLogin()
  .defaultSuccessUrl("/home", true)

Если параметр alwaysUse не установлен или имеет значение false, пользователь будет перенаправлен на предыдущую страницу, к которой он пытался получить доступ, прежде чем ему будет предложено пройти аутентификацию.

Точно так же мы можем указать пользовательскую целевую страницу отказа:

4.3. Авторизация

http.formLogin()
  .failureUrl("/auth/login?error=true")

Мы можем ограничить доступ к ресурсу по роли:

Пользователь, не являющийся администратором, получит сообщение об ошибке «Отказано в доступе», если попытается получить доступ к конечной точке /home/admin.

http.formLogin()
  .antMatchers("/home/admin*").hasRole("ADMIN")

Мы также можем ограничить данные на странице JSP в зависимости от роли пользователя. Это делается с помощью тега \u003csecurity:authorize\u003e:

Чтобы использовать этот тег, мы должны включить библиотеку тегов Spring Security вверху страницы:

<security:authorize access="hasRole('ADMIN')">
    This text is only visible to an admin
    <br/>
    <a href="<c:url value="/home/admin" />">Admin Page</a>
    <br/>
</security:authorize>

5. Spring Security XML Конфигурация

<%@ taglib prefix="security" 
  uri="http://www.springframework.org/security/tags" %>

До сих пор мы рассматривали настройку Spring Security в Java. Давайте взглянем на эквивалентную конфигурацию XML.

Во-первых, нам нужно создать файл security.xml в папке web/WEB-INF/spring, который содержит наши XML-конфигурации. Пример такого конфигурационного файла security.xml доступен в конце статьи.

Начнем с настройки менеджера аутентификации и провайдера аутентификации. Для простоты мы используем простые жестко заданные учетные данные пользователя:

Мы только что создали пользователя с именем пользователя, паролем и ролью.

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user" 
              password="user123" 
              authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

Кроме того, мы можем настроить нашего провайдера аутентификации с кодировщиком паролей:

Мы также можем указать пользовательскую реализацию Spring UserDetailsService или Datasource в качестве нашего провайдера аутентификации. Более подробную информацию можно найти здесь.

<authentication-manager>
    <authentication-provider>
        <password-encoder hash="sha"/>
        <user-service>
            <user name="user"
              password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" 
              authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

Теперь, когда мы настроили менеджер аутентификации, давайте настроим правила безопасности и применим контроль доступа:

«

<http auto-config='true' use-expressions="true">
    <form-login default-target-url="/secure.jsp" />
    <intercept-url pattern="/" access="isAnonymous()" />
    <intercept-url pattern="/index.jsp" access="isAnonymous()" />
    <intercept-url pattern="/secure.jsp" access="hasRole('ROLE_USER')" />
</http>

«В приведенном выше фрагменте мы настроили HttpSecurity для использования формы входа и установили /secure.jsp в качестве URL-адреса успешного входа. Мы предоставили анонимный доступ к /index.jsp и пути «/». Кроме того, мы указали, что доступ к /secure.jsp должен требовать аутентификации, а аутентифицированный пользователь должен иметь как минимум уровень полномочий ROLE_USER.

Установка для атрибута auto-config тега http значения true указывает Spring Security реализовать поведение по умолчанию, которое нам не нужно переопределять в конфигурации. Поэтому /login и /logout будут использоваться для входа и выхода пользователя соответственно. Также предоставляется страница входа по умолчанию.

Мы можем дополнительно настроить тег form-login с пользовательскими страницами входа и выхода, URL-адресами для обработки как неудачной, так и успешной аутентификации. В приложении Security Namespace перечислены все возможные атрибуты для тегов form-login (и других). Некоторые IDE также позволяют проводить проверку, щелкая тег при нажатой клавише ctrl.

Наконец, чтобы конфигурация security.xml загружалась во время запуска приложения, нам нужно добавить следующие определения в наш web.xml:

<context-param>                                                                           
    <param-name>contextConfigLocation</param-name>                                        
    <param-value>                                                                         
      /WEB-INF/spring/*.xml                                                             
    </param-value>                                                                        
</context-param>                                                                          
                                                                                          
<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>                                                                         
                                                                                          
<listener>                                                                                
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

Обратите внимание, что попытка использовать конфигурации на основе XML и Java в одно и то же приложение JEE может вызвать ошибки.

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

В этой статье мы рассмотрели, как защитить приложение Jakarta EE с помощью Spring Security, и продемонстрировали конфигурации на основе Java и XML.

Мы также обсудили способы предоставления или отзыва доступа к определенным ресурсам в зависимости от роли пользователя.

Полный исходный код и определения XML доступны на GitHub.