Spring: Security

Spring: Security


Важно выделить следующие определения:

  • Principal. Субъект, который предполагает выполнение действия;
  • Credentials. Идентификационные данные, которые исползует Principal для идентификации;
  • Authentication. Процесс проверки валидности Credentials;
  • Authorization. Процесс, определяющий возможность для юзера выполнить конкретное действие;
  • Secured (item). Любой ресурс, требующий проверки доступности.

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

  • Basic;
  • Form;
  • OAuth;
  • X.509;
  • Cookie;
  • Single-Sign-On.

Некоторые допустимые места хранения Credentials:

  • DAP;
  • RDBMS;
  • Binary Files;
  • Custom DAOs.

Сперва выполняется Authentication с использованием Credentials для запроса Secured (item), который требует соответствующую Authorization.

Наиболее часто встречающиеся роли:

  • ADMIN. Полные полномочия;
  • MEMBER. Может изменять только доступные, согласно роли, данные;
  • GUEST. Может только просматривать данные. Просмотр так же с ограничением.

Конфигурирование Spring Security включает:

  • Определение фильтра безопасности;
  • Отпределение Spring Security контекста;
  • Настройка аутентификации и авторизации.

Основные фильтры:

  • ChannelProcessingFilter. Исползуется если требуется перенаправление на другой протокол. Константа CHANNEL_FILTER;
  • SecurityContextPersistenceFilter. Используется для настройки контекста безопасности и копирует изменения из HttpSession. Константа SECURITY_CONTEXT_FILTER;
  • ConcurrentSessionFilter. Исползуется для пакета многопоточной обработки сессий. Константа CONCURRENT_SESSION_FILTER;
  • BasicAuthenticationFilter. Хранит Authentication в Security контексте. Константа BASIC_AUTHENTICATION_FILTER;
  • JaasApiIntegrationFilter. Выполняет попытки получения JAAS объект для использования его как субъект (Subject) в FilterChain. Константа JAAS_API_SUPPORT_FILTER;
  • RememberMeAuthenticationFilter. Хранит и использует Authentication, если не было обнаружено каких-либо изменений. Константа REMEMBER_ME_FILTER;
  • AnonymousAuthenticationFilter. Хранит анонимную Authentication и использует ее если не было изменений. Константа ANONYMOUS_FILTER;
  • ExceptionTranslatorFilter. Преобразует Java исключения в соответствующие HTTP ошибки;
  • FilterSecurityInterceptor. Выкидывает ошибки в случае отсутствия доступа в URI. Константа FILTER_SECURITY_INTERCEPTOR.

Изменение цепочки аутентификации

В случае XML выполняется подмена бина в соответствии с позицией:

1
2
3
4
5
6
<beans:beans ...>
<http>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="myCustomConcurrentFilter"/>
<beans:bean id="myCustomConcurrentFilter" class="my.custome.filter.Concurrent"/>
</http>
</beans:beans ...>

В случае конфигурации через Java

1
2
3
4
5
6
7
8
9
10
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled=true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity sec) {
sec.addFilterBefore(myCustomFilter(), LogoutFilter.class);
sec.addFilterAfter(mySecondFilter(), JaasApiIntegrationFilter.class);
}
}

Ограничение доступа

В случае с XML - ограничение накладывается:

1
2
3
4
5
6
7
8
<beans:beans ...>
<http>
<intercept-html pattern="/auth" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-html pattern="/users/**" access="IS_AUTHENTICATED_FULLY"/>
<form-login ... />
<logout ... />
</http>
</beans:beans ...>

В атрибуте access можно указывать роли, например, ROLE_ADMIN, ROLE_USER, ADMIN, …

В XML важна очередность! Наиболее ограничивающие должны быть выше.

Spring Security EL

В Spring 3 появилась возможность упрощенного указания ограничений в XML:

1
2
3
4
5
<html use-expression="true">
<intercept-url ... access="permitAll()"/>
<intercept-url ... access="hasRole(‘ADMIN’)"/>
<intercept-url ... access="hasAnyRole(‘ADMIN’, ‘HEAD_ADMIN’, ...)"/>
</http>

, где:

  • hasRole(?) - проверка на 1 роль;
  • hasAnyRole(?, ?, ...) - проверка на хотя бы 1 роль;
  • isAnonymous() - является ли пользователь анонимом;
  • isAuthenticated() - является ли пользовать аутентифицированным;
  • hasIpAddress(?) - является ли клиент с IP-адресом.

Условия можно комбинировать, используя and

Spring Security через Java

Заменой XML конфигурации служит Java конфигурация. Для конфигурирования следует создать класс, наследуемый от WebSecurityConfigurerAdapter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configureGlobal(AuthenticationManagerBuilder authBuilder) {
try {
PasswordEncoder enc = new DCryptPasswordEncoder();
authBuilder.inMemoryAuthentication().passwordEncoder(enc).withUser("testUser").password(enc.encode(“testUserPassword")).roles("USER", "ADMIN").and()...;
} catch(Exception e) {
...
}
}

@Override
protected void configure(HttpSecurity sec) throws Exception {
http.permitAll().antMatchers("users/*/edit/**").hasRole("ADMIN").csrf().csrfTokenRepository(csrfToken());
}

@Override
protected void configure(WebSecurity web) throws Exception {
web.ignoring().andMatchers("**/*.font", "images/**", "styles/**");
}

@Bean
Public CsfrTokenRepository csftToken() {
HttpSessionCsfrTokenRepository repo = new HttpSessionCsfrTokenRepository();
repo.setParameterName("_csft");
repo.setHeaderName("X-CSFR-TOKEN");
return repo;
}
}

mvcMatcher

Является аналогом antMatcher, но в большей степени рекомендован к использованию.

Spring Security Method

В качестве альтернативы XML конфигурации, Spring позволяет добавлять аннотацию @Secured на метод, подлежащий ограничениям. Для этого к конфигурационному компоненту добавить @EnableGlobalMethodSecurity(securedEnabled=true):

1
2
3
4
5
6
7
8
9
10
11
12
@Configurection
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class MySecConfig extends WebSecurityConfigurerAdapter {
...
}

@Service
public class UserService {
@Secured("ROLE_ADMIN")
public List<User> findAll(...) {...}
}

Вторым вариантом аннотирования безопасности Spring является:

1
2
3
4
5
6
7
8
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled=true)
public class MySecConfig {
...
}

@RolesAllowed("ROLE_ADMIN")
public List<User> findAll(...) {...}

Перехват авторизации/фильтров

Spring позволяет добавить перехват Security событий. Для этого их необходимо включить:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MySecConfig extends WebSecurityConfigurerAdapter {
...
}

@Service
public class UserService {
@PreAuthorize("hasRole(‘USER’)")
public void finalAll(...) {...}
}

Доступные аннотации:

  • @PreAuthorize;
  • @PostAuthorize;
  • @PreFilter;
  • @PostFilter.

Для определения условий для аннотаций используется SPeL и встраивается в Spring Security ACL (Access Control List, список разрешений на объект - какой роли, какому пользователю, какой объект будет доступен).

Пример аутентификации

Для примера добавим бин с примитивной аутентификацией с использованием сочетания логин-пароль и реализованной на стороне Spring. После удачной аутентификации в Spring Security, вызываются специфические перехватчики события успешной (или не успешной) аутентификациичерез форму.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
public class AuthSuccessHandler extends SimpleAuthenticationSuccessHandler {
private RequestCache cache = new HttpSessionRequestCache();

@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) {
try {
SavedRequest cachedRequest = cache.getRequest(req, res);
if(cachedRequest == null) Return;

String urlParam = getTargetUrlParam();
if(isAlwaysUseDefaultTargetUrl() || (urlParam != null && request.getParameter(urlParam) != null)) cache.removeRequest(req, res);
} finally {
clearAuthenticationAttributes(req);
}
}
}

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity sec) {
sec.exceptionHandling()
.accessDeniedHandler(..)
.authenticationEntryPoint(..)
.and()
.authorizeRequests()
.mvcMatchers("...").hasAnyRole(..)
.formLogin()
.successHandler(myCustomSuccessAuth())
.failureHandler(myCustomFailureAuth())
}
}

Стандарты аутентификации

Для аутентификации с использованием формы, Spring предполагает передачу пары логин/пароль. Плохой практикой является передача пароля в открытом виде - по-этому в Spring реализована возможность использования алгоритмов хэширования пароля. Самой простой реализацией является MD5Spring существует некоторое количество готовых реализаций подсчета хэш сумм). Фактически, на стороне сервера хранится не сам пароль, а его хэш сумма и при передаче запроса на аутентификацию, проверяется соответствие именно этой хэш суммы. Пароль невозможно восстановить, можно лишь сгенерировать новую хэш-сумму (фактически, сгенерировать новый пароль). Детальнее тут.

Аутентификация с использованием выпущенного сертификата. Данный механизм представляет собой обмен данных в зашифрованном виде при наличии у клиента выпущенного (иногда личного) цифрового сертификата.

Наиболее часто применяемый в новых системах подход к аутентификации - это OAuth 2.0. Предполагает получение токена аутентификации с дальнейшее использование его в качестве подтверждения достоверности запроса. Данный механизм очень выгоден, поскольку подразумевает возможность работы как в режиме C2S (клиент-сервер), так и в режиме S2S (сервер-сервер). Инструменты аутентификации зачастую используются уже реализованные (например, KeyCloak), что высвобождает и сбрасывает ответственность за безопасность системы (часть) на техническую поддержку продукта, выполняющего аутентификацию.

 Comments
Comment plugin failed to load
Loading comment plugin