Pengenalan ke Spring Security Expressions

1. Perkenalan

Dalam tutorial ini kita akan fokus pada Spring Security Expressions, dan tentu saja pada contoh praktis dengan ekspresi ini.

Sebelum melihat implementasi yang lebih kompleks (seperti ACL), penting untuk memiliki pemahaman yang kuat tentang ekspresi keamanan - karena mereka bisa sangat fleksibel dan kuat jika digunakan dengan benar.

Artikel ini merupakan perpanjangan dari Spring Security Expressions - Contoh hasRole.

2. Ketergantungan Maven

Untuk menggunakan Spring Security, Anda perlu memasukkan bagian berikut ini ke dalam file pom.xml Anda :

  org.springframework.security spring-security-web 5.2.3.RELEASE  

Versi terbaru dapat ditemukan di sini.

Dan catatan singkat - ketergantungan ini hanya mencakup Keamanan Musim Semi; jangan lupa untuk menambahkan s pring-core dan spring-context untuk aplikasi web lengkap.

3. Konfigurasi

Pertama, mari kita lihat konfigurasi Java.

Kami akan memperluas WebSecurityConfigurerAdapter - sehingga kami memiliki opsi untuk menghubungkan ke salah satu titik ekstensi yang ditawarkan kelas dasar:

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

Kami tentu saja dapat melakukan konfigurasi XML juga:

4. Ekspresi Keamanan Web

Sekarang, mari kita mulai melihat ekspresi keamanan:

  • hasRole , hasAnyRole
  • hasAuthority , hasAnyAuthority
  • allowAll , denyAll
  • isAnonymous , isRememberMe , isAuthenticated , isFullyAuthenticated
  • pokok , otentikasi
  • hasPermission

Dan sekarang mari kita bahas masing-masing secara mendetail.

4.1. hasRole, hasAnyRole

Ekspresi ini bertanggung jawab untuk menentukan kontrol akses atau otorisasi ke URL atau metode tertentu dalam aplikasi Anda.

Mari kita lihat contohnya:

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

Dalam contoh ini kami menetapkan akses ke semua tautan yang dimulai dengan / auth / dibatasi untuk pengguna yang masuk dengan peran PENGGUNA atau peran ADMIN. Selain itu, untuk mengakses tautan yang dimulai dengan / auth / admin / kita perlu memiliki peran ADMIN dalam sistem.

Konfigurasi yang sama dapat dicapai dalam file XML, dengan menulis:

4.2. hasAuthority, hasAnyAuthority

Peran dan otoritas serupa di Spring.

Perbedaan utamanya adalah, peran memiliki semantik khusus - dimulai dengan Keamanan Musim Semi 4, awalan ' ROLE_ ' otomatis ditambahkan (jika belum ada) dengan metode terkait peran apa pun.

Jadi hasAuthority ('ROLE_ADMIN') mirip dengan hasRole ('ADMIN') karena awalan ' ROLE_ ' ditambahkan secara otomatis.

Tetapi hal yang baik tentang menggunakan otoritas adalah kita tidak harus menggunakan prefiks ROLE_ sama sekali.

Berikut adalah contoh singkat saat kami mendefinisikan pengguna dengan otoritas tertentu:

@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"); } 

Kami kemudian tentu saja dapat menggunakan ekspresi otoritas ini:

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

Seperti yang bisa kami lihat - kami tidak menyebutkan peran sama sekali di sini. Selain itu, mulai Spring 5, kami membutuhkan kacang PasswordEncoder :

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

Akhirnya - tentu saja kita dapat mencapai fungsionalitas yang sama menggunakan konfigurasi XML juga:

Dan:

4.3. allowAll, denyAll

Kedua anotasi tersebut juga cukup jelas. Kami mungkin mengizinkan akses ke beberapa URL di layanan kami atau kami mungkin menolak akses.

Mari kita lihat contohnya:

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

Dengan konfigurasi ini, kami akan memberi otorisasi kepada semua pengguna (baik anonim maupun login) untuk mengakses halaman yang dimulai dengan '/' (misalnya, beranda kami).

Kami juga dapat menolak akses ke seluruh ruang URL kami:

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

Dan sekali lagi, konfigurasi yang sama juga dapat dilakukan dengan konfigurasi XML:

4.4. isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated

In this subsection we focus on expressions related to login status of the user. Let's start with user that didn't log in to our page. By specifying following in Java config, we enable all unauthorized users to access our main page:

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

The same in XML config:

If we want to secure the website that everybody who uses it will be required to login, we need to use isAuthenticated() method:

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

or XML version:

Moreover, we have two additional expressions, isRememberMe() and isFullyAuthenticated(). Through the use of cookies, Spring enables remember-me capabilities so there is no need to log into the system each time. You can read more about Remember Me here.

In order to give the access to users that were logged in only by remember me function, we may use this:

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

or XML version:

Finally, some parts of our services require the user to be authenticated again even if the user is already logged in. For example, user wants to change settings or payment information; it's of course good practice to ask for manual authentication in the more sensitive areas of the system.

In order to do that, we may specify isFullyAuthenticated(), which returns true if the user is not an anonymous or a remember-me user:

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

or the XML version:

4.5. principal, authentication

These expressions allow accessing the principal object representing the current authorized (or anonymous) user and the current Authentication object from the SecurityContext, respectively.

We can, for example, use principal to load a user's email, avatar, or any other data that is accessible in the logged in user.

And authentication provides information about the full Authentication object, along with its granted authorities.

Both are described in further detail in the following article: Retrieve User Information in Spring Security.

4.6. hasPermission APIs

This expression is documented and intended to bridge between the expression system and Spring Security’s ACL system, allowing us to specify authorization constraints on individual domain objects, based on abstract permissions.

Let's have a look at an example. We have a service that allows cooperative writing articles, with a main editor, deciding which article proposed by other authors should be published.

In order to allow usage of such a service, we may create following methods with access control methods:

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

Only authorized user can call this method, and also user needs to have permission isEditor in the service.

We also need to remember to explicitly configure a PermissionEvaluator in our application context:

di mana customInterfaceImplementation akan menjadi kelas yang mengimplementasikan PermissionEvaluator.

Tentu saja kami juga dapat melakukan ini dengan konfigurasi Java:

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

5. Kesimpulan

Tutorial ini adalah pengenalan dan panduan komprehensif untuk Spring Security Expressions.

Semua contoh yang dibahas di sini tersedia di proyek GitHub.