Spring Security: Erkunden der JDBC-Authentifizierung

Ausdauer oben

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

In diesem kurzen Tutorial werden die von Spring angebotenen Funktionen zum Durchführen der JDBC-Authentifizierung unter Verwendung einer vorhandenen DataSource- Konfiguration erläutert .

In unserer Authentifizierung mit einem datenbankgestützten UserDetailsService-Beitrag haben wir einen Ansatz analysiert, um dies zu erreichen, indem wir die UserDetailService- Schnittstelle selbst implementiert haben.

Dieses Mal verwenden wir die Anweisung AuthenticationManagerBuilder # jdbcAuthentication , um die Vor- und Nachteile dieses einfacheren Ansatzes zu analysieren.

2. Verwenden einer eingebetteten H2-Verbindung

Zunächst analysieren wir, wie wir mithilfe einer eingebetteten H2-Datenbank eine Authentifizierung erreichen können.

Dies ist einfach zu erreichen, da die meisten Autokonfigurationen des Spring Boot sofort für dieses Szenario vorbereitet sind.

2.1. Abhängigkeiten und Datenbankkonfiguration

Beginnen wir damit, den Anweisungen unseres vorherigen Beitrags zu Spring Boot With H2 Database zu folgen:

  1. Fügen Sie die entsprechenden Feder-boot-Starter-data-JPA und h2 Abhängigkeiten
  2. Konfigurieren Sie die Datenbankverbindung mit Anwendungseigenschaften
  3. Aktivieren Sie die H2-Konsole

2.2. JDBC-Authentifizierung konfigurieren

Wir verwenden den AuthenticationManagerBuilder- Konfigurationshelfer von Spring Security, um die JDBC-Authentifizierung zu konfigurieren:

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

Wie wir sehen können, verwenden wir die automatisch konfigurierte DataSource. Die Anweisung withDefaultSchema fügt ein Datenbankskript hinzu, das das Standardschema auffüllt und das Speichern von Benutzern und Berechtigungen ermöglicht.

Dieses grundlegende Benutzerschema ist im Spring Security-Anhang dokumentiert.

Schließlich erstellen wir programmgesteuert einen Eintrag in der Datenbank mit einem Standardbenutzer.

2.3. Überprüfen der Konfiguration

Erstellen wir einen sehr einfachen Endpunkt, um die authentifizierten Principal- Informationen abzurufen :

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

Darüber hinaus sichern wir diesen Endpunkt und gewähren gleichzeitig Zugriff auf die H2-Konsole:

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

Hinweis: Hier reproduzieren wir die frühere Sicherheitskonfiguration, die von Spring Boot implementiert wurde. In einem realen Szenario wird die H2-Konsole jedoch wahrscheinlich überhaupt nicht aktiviert.

Jetzt führen wir die Anwendung aus und durchsuchen die H2-Konsole. Wir können überprüfen, ob Spring zwei Tabellen in unserer eingebetteten Datenbank erstellt: Benutzer und Berechtigungen.

Ihre Struktur entspricht der Struktur, die im zuvor erwähnten Spring Security-Anhang definiert ist.

Zuletzt authentifizieren wir den / Principal- Endpunkt und fordern ihn an, die zugehörigen Informationen einschließlich der Benutzerdetails anzuzeigen.

2.4. Unter der Haube

Zu Beginn dieses Beitrags haben wir einen Link zu einem Lernprogramm vorgestellt, in dem erläutert wird, wie wir die datenbankgestützte Authentifizierung mithilfe der UserDetailsService- Schnittstelle anpassen können . Wir empfehlen dringend, einen Blick auf diesen Beitrag zu werfen, wenn wir verstehen wollen, wie die Dinge unter der Haube funktionieren.

In diesem Fall verlassen wir uns auf eine Implementierung derselben Schnittstelle, die von Spring Security bereitgestellt wird. die JdbcDaoImpl .

Wenn wir diese Klasse untersuchen, sehen wir die verwendete UserDetails- Implementierung und die Mechanismen zum Abrufen von Benutzerinformationen aus der Datenbank.

Dies funktioniert für dieses einfache Szenario recht gut, hat jedoch einige Nachteile, wenn wir das Datenbankschema anpassen möchten oder wenn wir einen anderen Datenbankanbieter verwenden möchten.

Mal sehen, was passiert, wenn wir die Konfiguration ändern, um einen anderen JDBC-Dienst zu verwenden.

3. Anpassen des Schemas für eine andere Datenbank

In diesem Abschnitt konfigurieren wir die Authentifizierung für unser Projekt mithilfe einer MySQL-Datenbank.

Wie wir als nächstes sehen werden, müssen wir, um dies zu erreichen, die Verwendung des Standardschemas vermeiden und unser eigenes bereitstellen.

3.1. Abhängigkeiten und Datenbankkonfiguration

Entfernen wir zunächst die h2- Abhängigkeit und ersetzen sie durch die entsprechende MySQL-Bibliothek:

 mysql mysql-connector-java 8.0.17 

Wie immer können wir die neueste Version der Bibliothek in Maven Central nachschlagen.

Lassen Sie uns nun die Anwendungseigenschaften entsprechend zurücksetzen:

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

3.2. Ausführen der Standardkonfiguration

Natürlich sollten diese angepasst werden, um eine Verbindung zu Ihrem laufenden MySQL-Server herzustellen. Zu Testzwecken starten wir hier eine neue Instanz mit Docker:

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

Lassen Sie uns das Projekt jetzt ausführen, um festzustellen, ob die Standardkonfiguration für eine MySQL-Datenbank geeignet ist.

Tatsächlich kann die Anwendung aufgrund einer SQLSyntaxErrorException nicht gestartet werden . Das macht tatsächlich Sinn; Wie gesagt, der größte Teil der Standard-Autokonfiguration ist für eine HSQLDB geeignet.

In diesem Fall verwendet das mit der withDefaultSchema- Direktive bereitgestellte DDL-Skript einen Dialekt, der für MySQL nicht geeignet ist.

Daher müssen wir die Verwendung dieses Schemas vermeiden und unser eigenes bereitstellen.

3.3. Anpassen der Authentifizierungskonfiguration

Da wir das Standardschema nicht verwenden möchten, müssen wir die richtige Anweisung aus der AuthenticationManagerBuilder- Konfiguration entfernen .

Da wir unsere eigenen SQL-Skripte bereitstellen, können wir außerdem vermeiden, den Benutzer programmgesteuert zu erstellen:

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

Schauen wir uns nun die Datenbankinitialisierungsskripte an.

Zunächst unsere schema.sql :

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);

Und dann unsere data.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');

Schließlich sollten wir einige andere Anwendungseigenschaften ändern:

  • 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

Wie wir sehen können, ist dieser Ansatz viel einfacher als das Erstellen eines eigenen UserDetailServiceImplementierung, die einen mühsamen Prozess impliziert; Erstellen von Entitäten und Klassen, die die UserDetail- Schnittstelle implementieren , und Hinzufügen von Repositorys zu unserem Projekt.

Der Nachteil ist natürlich die geringe Flexibilität, die es bietet, wenn sich unsere Datenbank oder unsere Logik von der Standardstrategie der Spring Security-Lösung unterscheidet.

Zuletzt können wir uns die vollständigen Beispiele in unserem GitHub-Repository ansehen. Wir haben sogar ein Beispiel mit PostgreSQL aufgenommen, das wir in diesem Tutorial nicht gezeigt haben, nur um die Dinge einfach zu halten.

Persistenz unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs