Spring Security: Проучване на JDBC удостоверяване

Устойчивост отгоре

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА

1. Общ преглед

В този кратък урок ще проучим възможностите, предлагани от Spring за извършване на JDBC удостоверяване, използвайки съществуваща конфигурация на DataSource .

В нашето удостоверяване с публикация UserDetailsService, подкрепена с база данни, ние анализирахме един подход за постигане на това, като внедрихме сами интерфейса UserDetailService .

Този път ще използваме директивата AuthenticationManagerBuilder # jdbcAuthentication, за да анализираме плюсовете и минусите на този по-прост подход.

2. Използване на вградена H2 връзка

На първо място, ще анализираме как можем да постигнем удостоверяване чрез вградена база данни H2.

Това е лесно да се постигне, тъй като по-голямата част от автоматичното конфигуриране на Spring Boot са подготвени незабавно за този сценарий.

2.1. Зависимости и конфигурация на база данни

Нека започнем, като следваме инструкциите от предишната публикация на Spring Boot With H2 Database за:

  1. Включете съответните зависимости spring-boot-starter-data-jpa и h2
  2. Конфигурирайте връзката на базата данни със свойства на приложението
  3. Активирайте конзолата H2

2.2. Конфигуриране на JDBC удостоверяване

Ще използваме помощника за конфигуриране на AuthenticationManagerBuilder на Spring Security за конфигуриране на 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(); }

Както виждаме, използваме автоматично конфигурирания DataSource. В withDefaultSchema директивата добавя скрипт база данни, която ще се пренесат схемата по подразбиране, което позволява на потребителите и властите трябва да се съхраняват.

Тази основна потребителска схема е документирана в приложението Spring Security.

И накрая, ние създаваме запис в базата данни с потребител по подразбиране програмно.

2.3. Проверка на конфигурацията

Нека създадем много проста крайна точка за извличане на удостоверената основна информация:

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

Освен това ще защитим тази крайна точка, като същевременно разрешим достъп до конзолата 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(); } }

Забележка: тук възпроизвеждаме предишната конфигурация на защитата, внедрена от Spring Boot, но в реален сценарий вероятно изобщо няма да активираме конзолата H2.

Сега ще стартираме приложението и ще разгледаме конзолата H2. Можем да проверим дали Spring създава две таблици във вградената ни база данни: потребители и органи.

Тяхната структура съответства на структурата, дефинирана в Приложението за пролетна сигурност, което споменахме преди.

И накрая, нека се удостоверим и изискаме / основната крайна точка, за да видим свързаната информация, включително подробностите за потребителя.

2.4. Под капака

В началото на тази публикация представихме връзка към урок, който обяснява как можем да персонализираме удостоверяване, подкрепено с база данни, изпълняващо интерфейса UserDetailsService ; силно препоръчваме да разгледате този пост, ако искаме да разберем как работят нещата под капака.

В този случай разчитаме на изпълнение на същия интерфейс, предоставен от Spring Security; на JdbcDaoImpl .

Ако изследваме този клас, ще видим реализацията UserDetails, която използва, и механизмите за извличане на потребителска информация от базата данни.

Това работи доста добре за този прост сценарий, но има някои недостатъци, ако искаме да персонализираме схемата на базата данни или дори ако искаме да използваме различен доставчик на база данни.

Нека видим какво ще се случи, ако променим конфигурацията, за да използваме различна JDBC услуга.

3. Адаптиране на схемата за различна база данни

В този раздел ще конфигурираме удостоверяване на нашия проект, използвайки база данни MySQL.

Както ще видим по-нататък, за да постигнем това, ще трябва да избягваме използването на схемата по подразбиране и да предоставим нашата собствена.

3.1. Зависимости и конфигурация на база данни

Като начало, нека премахнем зависимостта h2 и я заменим за съответната библиотека на MySQL:

 mysql mysql-connector-java 8.0.17 

Както винаги, можем да потърсим най-новата версия на библиотеката в Maven Central.

Сега нека пренастроим съответно свойствата на приложението:

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

3.2. Стартиране на конфигурацията по подразбиране

Разбира се, те трябва да бъдат персонализирани, за да се свържат с вашия работещ MySQL сървър. За целите на тестването тук ще стартираме нов екземпляр с помощта на Docker:

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

Нека стартираме проекта сега, за да видим дали конфигурацията по подразбиране е подходяща за база данни MySQL.

Всъщност приложението няма да може да започне поради SQLSyntaxErrorException . Това всъщност има смисъл; както казахме, по-голямата част от автоматичното конфигуриране по подразбиране е подходящо за HSQLDB.

In this case, the DDL script provided with the withDefaultSchema directive uses a dialect not suitable for MySQL.

Therefore, we need to avoid using this schema and provide our own.

3.3. Adapting the Authentication Configuration

As we don't want to use the default schema, we'll have to remove the proper statement from the AuthenticationManagerBuilder configuration.

Also, since we'll be providing our own SQL scripts, we can avoid trying to create the user programmatically:

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

Now let's have a look at the database initialization scripts.

First, our 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);

And then, our 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');

Finally, we should modify some other application properties:

  • 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

Както виждаме, този подход е много по-прост, отколкото да се налага да създаваме своя собствена UserDetailServiceизпълнение, което предполага труден процес; създаване на обекти и класове, изпълняващи интерфейса UserDetail и добавяне на хранилища към нашия проект.

Недостатъкът е, разбира се, малката гъвкавост, която предлага, когато нашата база данни или нашата логика се различават от стратегията по подразбиране, предоставена от решението Spring Security.

И накрая, можем да разгледаме пълните примери в нашето хранилище на GitHub. Дори включихме пример с помощта на PostgreSQL, който не показахме в този урок, само за да улесним нещата.

Устойчивост отдолу

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА