«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.