Keselamatan Spring: Meneroka Pengesahan JDBC

Ketekunan atas

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS

1. Gambaran keseluruhan

Dalam tutorial ringkas ini, kita akan meneroka keupayaan yang ditawarkan oleh Spring untuk melakukan Pengesahan JDBC menggunakan konfigurasi DataSource yang ada .

Dalam pengesahan kami dengan catatan UserDetailsService yang disokong oleh Pangkalan Data, kami menganalisis satu pendekatan untuk mencapainya, dengan menerapkan antara muka UserDetailService .

Kali ini, kami akan menggunakan arahan AuthenticationManagerBuilder # jdbcAuthentication untuk menganalisis kebaikan dan keburukan pendekatan yang lebih mudah ini.

2. Menggunakan Sambungan H2 Terbenam

Pertama sekali, kami akan menganalisis bagaimana kami dapat mencapai pengesahan menggunakan pangkalan data H2 terbenam.

Ini mudah dicapai kerana sebahagian besar konfigurasi automatik Spring Boot disiapkan secara luar biasa untuk senario ini.

2.1. Ketergantungan dan Konfigurasi Pangkalan Data

Mari mulakan dengan mengikuti arahan dari Spring Boot With H2 Database post kami sebelum ini ke:

  1. Sertakan pergantungan spring-boot-starter-data-jpa dan h2 yang sesuai
  2. Konfigurasikan sambungan pangkalan data dengan sifat aplikasi
  3. Dayakan konsol H2

2.2. Mengkonfigurasi Pengesahan JDBC

Kami akan menggunakan pembantu konfigurasi Spring Security's AuthenticationManagerBuilder untuk mengkonfigurasi Pengesahan JDBC:

@Autowired private DataSource dataSource; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .withDefaultSchema() .withUser(User.withUsername("user") .password(passwordEncoder().encode("pass")) .roles("USER")); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }

Seperti yang dapat kita lihat, kita menggunakan DataSource yang dikonfigurasi secara automatik. The withDefaultSchema arahan menambah skrip pangkalan data yang akan mengisi skema lalai, yang membolehkan pengguna dan pihak berkuasa untuk disimpan.

Skema pengguna asas ini didokumentasikan dalam Lampiran Keselamatan Musim Semi.

Akhirnya, kami membuat entri dalam pangkalan data dengan pengguna lalai secara terprogram.

2.3. Mengesahkan Konfigurasi

Mari buat titik akhir yang sangat mudah untuk mendapatkan maklumat Pengetua yang disahkan :

@RestController @RequestMapping("/principal") public class UserController { @GetMapping public Principal retrievePrincipal(Principal principal) { return principal; } }

Di samping itu, kami akan melindungi titik akhir ini, sambil membenarkan akses ke konsol H2:

@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeRequests() .antMatchers("/h2-console/**") .permitAll() .anyRequest() .authenticated() .and() .formLogin(); httpSecurity.csrf() .ignoringAntMatchers("/h2-console/**"); httpSecurity.headers() .frameOptions() .sameOrigin(); } }

Catatan: di sini kami menghasilkan semula konfigurasi keselamatan yang dilaksanakan oleh Spring Boot, tetapi dalam senario kehidupan sebenar, kami mungkin tidak akan mengaktifkan konsol H2 sama sekali.

Sekarang kita akan menjalankan aplikasi dan melayari konsol H2. Kami dapat mengesahkan bahawa Spring membuat dua jadual dalam pangkalan data terbenam kami: pengguna dan pihak berkuasa.

Struktur mereka sesuai dengan struktur yang ditentukan dalam Lampiran Keselamatan Musim Semi yang telah kita sebutkan sebelumnya.

Akhirnya, mari kita mengesahkan dan meminta titik akhir / prinsipal untuk melihat maklumat yang berkaitan, termasuk perincian pengguna.

2.4. Di bawah Tudung

Pada awal catatan ini, kami menunjukkan pautan ke tutorial yang menjelaskan bagaimana kami dapat menyesuaikan pengesahan yang disokong pangkalan data yang melaksanakan antara muka UserDetailsService ; kami sangat mengesyorkan untuk melihat siaran itu jika kami ingin memahami bagaimana perkara-perkara berfungsi di bawahnya.

Dalam kes ini, kami bergantung pada pelaksanaan antara muka yang sama yang disediakan oleh Spring Security; yang JdbcDaoImpl .

Sekiranya kita menjelajahi kelas ini, kita akan melihat pelaksanaan UserDetails yang digunakannya, dan mekanisme untuk mengambil maklumat pengguna dari pangkalan data.

Ini berfungsi dengan baik untuk senario sederhana ini, tetapi mempunyai beberapa kelemahan jika kita ingin menyesuaikan skema pangkalan data, atau bahkan jika kita ingin menggunakan vendor pangkalan data yang berbeza.

Mari lihat apa yang berlaku sekiranya kita mengubah konfigurasi untuk menggunakan perkhidmatan JDBC yang berbeza.

3. Mengadaptasi Skema untuk Pangkalan Data yang berbeza

Di bahagian ini, kami akan mengkonfigurasi pengesahan pada projek kami menggunakan pangkalan data MySQL.

Seperti yang akan kita lihat selanjutnya, untuk mencapainya, kita perlu mengelakkan penggunaan skema lalai dan menyediakan skema sendiri.

3.1. Ketergantungan dan Konfigurasi Pangkalan Data

Sebagai permulaan, mari kita hapus kebergantungan h2 dan gantinya untuk perpustakaan MySQL yang sesuai:

 mysql mysql-connector-java 8.0.17 

Seperti biasa, kami dapat melihat versi terbaru perpustakaan di Maven Central.

Sekarang mari kita tetapkan semula sifat aplikasi dengan sewajarnya:

spring.datasource.url= jdbc:mysql://localhost:3306/jdbc_authentication spring.datasource.username=root spring.datasource.password=pass

3.2. Menjalankan Konfigurasi Lalai

Sudah tentu, ini harus disesuaikan untuk menyambung ke pelayan MySQL anda yang sedang berjalan. Untuk tujuan ujian, di sini kita akan memulakan contoh baru menggunakan Docker:

docker run -p 3306:3306 --name bael-mysql -e MYSQL_ROOT_PASSWORD=pass -e MYSQL_DATABASE=jdbc_authentication mysql:latest

Mari jalankan projek sekarang untuk melihat apakah konfigurasi lalai sesuai untuk pangkalan data MySQL.

Sebenarnya, aplikasi ini tidak dapat dimulakan, kerana adanya SQLSyntaxErrorException . Ini sebenarnya masuk akal; seperti yang kami katakan, sebahagian besar konfigurasi autocontoh sesuai untuk HSQLDB.

Dalam kes ini, skrip DDL yang disertakan dengan arahan withDefaultSchema menggunakan dialek yang tidak sesuai untuk MySQL.

Oleh itu, kita perlu mengelakkan penggunaan skema ini dan menyediakan skema kita sendiri.

3.3. Mengadaptasi Konfigurasi Pengesahan

Oleh kerana kami tidak mahu menggunakan skema lalai, kami harus membuang pernyataan yang betul dari konfigurasi AuthenticationManagerBuilder .

Juga, kerana kami akan menyediakan skrip SQL kami sendiri, kami dapat mengelakkan daripada membuat pengguna secara terprogram:

@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource); }

Sekarang mari kita lihat skrip inisialisasi pangkalan data.

Pertama, schema.sql kami :

CREATE TABLE users ( username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, enabled TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (username) ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, FOREIGN KEY (username) REFERENCES users(username) ); CREATE UNIQUE INDEX ix_auth_username on authorities (username,authority);

Dan kemudian, data kami.sql :

-- User user/pass INSERT INTO users (username, password, enabled) values ('user', '$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a', 1); INSERT INTO authorities (username, authority) values ('user', 'ROLE_USER');

Akhirnya, kita harus mengubah beberapa sifat aplikasi lain:

  • Since we're not expecting Hibernate to create the schema now, we should disable the ddl-auto property
  • By default, Spring Boot initializes the data source only for embedded databases, which is not the case here:
spring.datasource.initialization-mode=always spring.jpa.hibernate.ddl-auto=none

As a result, we should now be able to start our application correctly, authenticating and retrieving the Principal data from the endpoint.

4. Adapting the Queries for a Different Schema

Let's go a step further. Imagine the default schema is just not suitable for our needs.

4.1. Changing the Default Schema

Imagine, for example, that we already have a database with a structure that slightly differs from the default one:

CREATE TABLE bael_users ( name VARCHAR(50) NOT NULL, email VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, enabled TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (email) ); CREATE TABLE authorities ( email VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, FOREIGN KEY (email) REFERENCES bael_users(email) ); CREATE UNIQUE INDEX ix_auth_email on authorities (email,authority);

Finally, our data.sql script will be adapted to this change too:

-- User [email protected]/pass INSERT INTO bael_users (name, email, password, enabled) values ('user', '[email protected]', '$2a$10$8.UnVuG9HHgffUDAlk8qfOuVGkqRzgVymGe07xd00DMxs.AQubh4a', 1); INSERT INTO authorities (email, authority) values ('[email protected]', 'ROLE_USER');

4.2. Running the Application with the New Schema

Let's launch our application. It initializes correctly, which makes sense since our schema is correct.

Now, if we try to log in, we'll find an error is prompted when presenting the credentials.

Spring Security is still looking for a username field in the database. Lucky for us, the JDBC Authentication configuration offers the possibility of customizing the queries used to retrieve user details in the authentication process.

4.3. Customizing the Search Queries

Adapting the queries is quite easy. We simply have to provide our own SQL statements when configuring the AuthenticationManagerBuilder:

@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery("select email,password,enabled " + "from bael_users " + "where email = ?") .authoritiesByUsernameQuery("select email,authority " + "from authorities " + "where email = ?"); }

We can launch the application once more, and access the /principal endpoint using the new credentials.

5. Conclusion

Seperti yang dapat kita lihat, pendekatan ini jauh lebih sederhana daripada harus membuat UserDetailService kita sendiripelaksanaan, yang membayangkan proses yang sukar; mewujudkan entiti dan kelas yang melaksanakan antara muka UserDetail dan menambahkan repositori ke projek kami.

Kekurangannya, tentu saja, sedikit fleksibiliti yang ditawarkannya ketika pangkalan data atau logik kita berbeza dengan strategi lalai yang disediakan oleh penyelesaian Spring Security.

Terakhir, kita dapat melihat contoh lengkap di repositori GitHub kami. Kami bahkan menyertakan contoh menggunakan PostgreSQL yang tidak kami tunjukkan dalam tutorial ini, hanya untuk menjaga kesederhanaan.

Ketekunan bawah

Saya baru sahaja mengumumkan kursus Learn Spring yang baru , yang berfokus pada asas-asas Spring 5 dan Spring Boot 2:

>> SEMAK KURSUS