Перейти к основному содержимому

Введение в выражения безопасности Spring

· 6 мин. чтения

1. Введение

В этом руководстве мы сосредоточимся на Spring Security Expressions и, конечно же, на практических примерах с этими выражениями.

Перед рассмотрением более сложных реализаций (таких как ACL) важно иметь четкое представление о выражениях безопасности, поскольку они могут быть достаточно гибкими и мощными при правильном использовании.

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

Чтобы использовать Spring Security, вам необходимо включить следующий раздел в файл pom.xml :

<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>

Последнюю версию можно найти здесь .

И небольшое примечание: эта зависимость распространяется только на Spring Security; не забудьте добавить s spring-core и spring-context для полноценного веб-приложения.

3. Конфигурация

Во-первых, давайте взглянем на конфигурацию Java.

Мы расширим WebSecurityConfigurerAdapter , чтобы у нас была возможность подключиться к любой из точек расширения, предлагаемых базовым классом:

@Configuration
@EnableAutoConfiguration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
...
}

Конечно, мы также можем выполнить конфигурацию XML:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans ...>
<global-method-security pre-post-annotations="enabled"/>
</beans:beans>

4. Выражения веб-безопасности

Теперь давайте начнем смотреть на выражения безопасности:

  • hasRole , hasAnyRole
  • hasAuthority , hasAnyAuthority
  • разрешить все , запретить все
  • isAnonymous , isRememberMe , isAuthenticated , isFullyAuthenticated
  • принципал , аутентификация
  • имеет разрешение

А теперь давайте подробно рассмотрим каждый из них.

4.1. hasRole, hasAnyRole

Эти выражения отвечают за определение контроля доступа или авторизации к определенным URL-адресам или методам в вашем приложении.

Давайте посмотрим на пример:

@Override
protected void configure(final HttpSecurity http) throws Exception {
...
.antMatchers("/auth/admin/*").hasRole("ADMIN")
.antMatchers("/auth/*").hasAnyRole("ADMIN","USER")
...
}

В этом примере мы указываем доступ ко всем ссылкам, начинающимся с /auth/ , только для пользователей, которые вошли в систему с ролью USER или ADMIN. Кроме того, для доступа к ссылкам, начинающимся с /auth/admin/ , нам необходимо иметь в системе роль ADMIN .

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

<http>
<intercept-url pattern="/auth/admin/*" access="hasRole('ADMIN')"/>
<intercept-url pattern="/auth/*" access="hasAnyRole('ADMIN','USER')"/>
</http>

4.2. hasAuthority, hasAnyAuthority

Роли и полномочия в Spring аналогичны.

Основное отличие состоит в том, что роли имеют особую семантику — начиная с Spring Security 4 префикс « ROLE_ » добавляется автоматически (если его еще нет) любым методом, связанным с ролью.

Таким образом , hasAuthority('ROLE_ADMIN') похож на hasRole('ADMIN'), потому что префикс ' ROLE_ ' добавляется автоматически.

Но преимущество использования полномочий заключается в том, что нам вообще не нужно использовать префикс ROLE_ .

Вот краткий пример, где мы определяем пользователей с определенными полномочиями:

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

Тогда мы, конечно, можем использовать следующие авторитетные выражения:

@Override
protected void configure(final HttpSecurity http) throws Exception {
...
.antMatchers("/auth/admin/*").hasAuthority("ADMIN")
.antMatchers("/auth/*").hasAnyAuthority("ADMIN", "USER")
...
}

Как видим, мы вообще не говорим о ролях. Кроме того, начиная с Spring 5, нам нужен bean-компонент PasswordEncoder :

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

Наконец, мы, конечно же, можем добиться той же функциональности, используя XML-конфигурацию:

<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1" password="user1Pass" authorities="ROLE_USER"/>
<user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
<bean name="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

А также:

<http>
<intercept-url pattern="/auth/admin/*" access="hasAuthority('ADMIN')"/>
<intercept-url pattern="/auth/*" access="hasAnyAuthority('ADMIN','USER')"/>
</http>

4.3. разрешить все, запретить все

Эти две аннотации также довольно просты. Мы можем либо разрешить доступ к какому-либо URL-адресу в нашем сервисе, либо отказать в доступе.

Давайте посмотрим на пример:

...
.antMatchers("/*").permitAll()
...

С помощью этой конфигурации мы разрешим всем пользователям (как анонимным, так и вошедшим в систему) доступ к странице, начинающейся с «/» (например, к нашей домашней странице).

Мы также можем запретить доступ ко всему нашему пространству URL:

...
.antMatchers("/*").denyAll()
...

И опять же, ту же конфигурацию можно выполнить и с конфигурацией XML:

<http auto-config="true" use-expressions="true">
<intercept-url access="permitAll" pattern="/*" /> <!-- Choose only one -->
<intercept-url access="denyAll" pattern="/*" /> <!-- Choose only one -->
</http>

4.4. isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated

В этом подразделе мы сосредоточимся на выражениях, связанных со статусом входа пользователя. Начнем с пользователя, который не авторизовался на нашей странице. Указав следующее в конфигурации Java, мы разрешаем всем неавторизованным пользователям доступ к нашей главной странице:

...
.antMatchers("/*").anonymous()
...

То же самое в конфигурации XML:

<http>
<intercept-url pattern="/*" access="isAnonymous()"/>
</http>

Если мы хотим защитить веб-сайт, чтобы каждый, кто его использует, должен был войти в систему, нам нужно использовать метод isAuthenticated() :

...
.antMatchers("/*").authenticated()
...

или XML-версия:

<http>
<intercept-url pattern="/*" access="isAuthenticated()"/>
</http>

Кроме того, у нас есть два дополнительных выражения, isRememberMe() и isFullyAuthenticated() . Благодаря использованию файлов cookie Spring позволяет запоминать меня, поэтому нет необходимости каждый раз входить в систему. Подробнее о «Помни меня » можно прочитать здесь .

Чтобы предоставить доступ пользователям, которые вошли в систему только с помощью функции «запомнить меня», мы можем использовать это:

...
.antMatchers("/*").rememberMe()
...

или XML-версия:

<http>
<intercept-url pattern="*" access="isRememberMe()"/>
</http>

Наконец, некоторые части наших услуг требуют повторной аутентификации пользователя, даже если он уже вошел в систему. Например, пользователь хочет изменить настройки или платежную информацию; конечно, рекомендуется запрашивать ручную аутентификацию в более чувствительных областях системы.

Для этого мы можем указать isFullyAuthenticated() , которая возвращает true , если пользователь не является анонимным или запоминающим пользователем:

...
.antMatchers("/*").fullyAuthenticated()
...

или XML-версия:

<http>
<intercept-url pattern="*" access="isFullyAuthenticated()"/>
</http>

4.5. принципал, аутентификация

Эти выражения позволяют получить доступ к основному объекту, представляющему текущего авторизованного (или анонимного) пользователя, и текущему объекту аутентификации из SecurityContext соответственно.

Мы можем, например, использовать принципал для загрузки электронной почты пользователя, аватара или любых других данных, которые доступны вошедшему в систему пользователю.

Аутентификация предоставляет информацию о полном объекте аутентификации вместе с предоставленными ему полномочиями. ``

Оба они более подробно описаны в следующей статье: Получить информацию о пользователе в Spring Security .

4.6. API- интерфейсы hasPermission

Это выражение задокументировано и предназначено для связи между системой выражений и системой ACL Spring Security, что позволяет нам указывать ограничения авторизации для отдельных объектов домена на основе абстрактных разрешений.

Давайте посмотрим на пример. У нас есть сервис, который позволяет совместно писать статьи с главным редактором, решая, какую статью, предложенную другими авторами, следует опубликовать.

Чтобы разрешить использование такой услуги, мы можем создать следующие методы с методами контроля доступа:

@PreAuthorize("hasPermission(#articleId, 'isEditor')")
public void acceptArticle(Article article) {

}

Только авторизованный пользователь может вызывать этот метод, а также пользователь должен иметь разрешение isEditor в службе.

Нам также нужно помнить о явной настройке PermissionEvaluator в контексте нашего приложения:

<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler"/>
</global-method-security>

<bean id="expressionHandler"
class="org.springframework.security.access.expression
.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="customInterfaceImplementation"/>
</bean>

где customInterfaceImplementation будет классом, реализующим PermissionEvaluator.

Конечно, мы также можем сделать это с конфигурацией Java:

@Override
protected MethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomInterfaceImplementation());
return expressionHandler;
}

5. Вывод

Это руководство представляет собой исчерпывающее введение и руководство по Spring Security Expressions.

Все обсуждаемые здесь примеры доступны на проекте GitHub .