Ранее мы рассмотрели In-Memory аутентификацию, пользовательскую аутентификацию. В этой статье рассмотрим JDBC-аутентификацию.
Поменяется только один метод:
configure(AuthenticationManagerBuilder auth)
в настройке Spring Security. Еще добавится стартер для работы в базой через JDBC.
Подготовка
Maven-зависимости
Итак, у нас веб-приложение с базой H2, так что добавим такие зависимости:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Контроллер
Контроллер с предыдущих примеров не поменялся:
@RestController public class HelloController { @GetMapping("/") public String hello() { return "Hello"; } @GetMapping("/user") public String user() { return "User"; } @GetMapping("/admin") public String admin() { return "Admin"; } }
Настройка Spring Security
Как обычно, чтобы настроить авторизацию и аутентификацию, нужно расширить класс WebSecurityConfigurerAdapter и переопределить два метода:
void configure(HttpSecurity http) для авторизации void configure(AuthenticationManagerBuilder auth) для аутентификации
Авторизация
Настройка авторизации та же самая, наша цель дать пользователям такие разрешения по таким url:
/ для всех пользователей (в том числе не аутентифицированных) /user для пользователей с ролью USER и ADMIN /admin для пользователей с ролью ADMIN
За авторизацию отвечает метод configure(HttpSecurity http):
@EnableWebSecurity(debug = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource).withDefaultSchema() .withUser(User.withUsername("user").password("password").roles("USER")) .withUser(User.withUsername("admin").password("password").roles("ADMIN")); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/user/**").hasAnyRole("USER", "ADMIN") .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/**").permitAll() .and().formLogin(); } }
А аутентификация, как видите, поменялась. За ее настройку отвечает метод configure(AuthenticationManagerBuilder auth). Ниже рассмотрим подробнее новую настройку.
JDBC-Аутентификация
Для JDBC-аутентификация в Spring Security предусмотрен метод auth.jdbcAuthentication():
@Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .withDefaultSchema() .withUser(User.withUsername("user").password("password").roles("USER")) .withUser(User.withUsername("admin").password("password").roles("ADMIN")); }
.dataSource(dataSource)
Источник данных задается этим методом , причем бин dataSource создавать не нужно, достаточно просто внедрить. Spring Boot его создает за нас.
Настраивать доступ к базе тоже не нужно, поскольку у нас встроенная In-Memory база H2, она настраивается автоматически.
.withDefaultSchema()
Означает, что схема, где хранятся пользователи, создается автоматически, и в нее добавляются пользователи, указанные далее. В документации есть эта схема, в ней две таблицы — пользователей и прав:
create table users( username varchar_ignorecase(50) not null primary key, password varchar_ignorecase(50) not null, enabled boolean not null ); create table authorities ( username varchar_ignorecase(50) not null, authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username) ); create unique index ix_auth_username on authorities (username,authority);
Вышеприведенный фрагмент кода можно трактовать так: Spring Securuty, создай нам в базе H2 свои стандартные таблицы и добавь туда двух пользователей с такими-то именами, паролями и правами.
Все, пример работает. Но немного пояснений.
Пояснения
При такой настройке аутентификации для получения пользователя используется JdbcDaoImpl (найти этот класс в Idea Intellij — Ctrl+N):
public class JdbcDaoImpl implements UserDetailsService...{ .... @Override public UserDetails loadUserByUsername(String username){...} }
То есть по сути это та же пользовательская аутентификация с UserDetailsService, которую мы делали, только специально для работы с базой через jdbc. И она в Spring Security уже написана. В этом методе Spring Security достает пользователя с правами из стандартных таблиц выше.
Если же таблицы не стандартные, а свои (так обычно и бывает), то нужно указать в настройках аутентификации свой SQL, с помощью которого Spring Security может извлечь пользователей с их правами из конкретно наших таблиц (или одной таблицы, если права хранятся с пользователями). Ниже мы это и сделаем.
Итак, допустим у нас уже есть свои таблицы с пользователями, и нам надо сказать Spring Security, как их извлечь (чтобы Spring Security мог сравнить реальный пароль с переданным).
JDBC-Аутентификация со своими таблицами
Подготовка
Для демонстрации примера надо создать свои таблицы и положить в них данные. Для этого создадим файл schema.sql. В нем создается таблица для хранения пользователей и прав, они хранятся у нас в одной таблице:
create table my_user( login varchar_ignorecase(50) not null primary key, password varchar_ignorecase(50) not null, position varchar_ignorecase(50) not null not null, authority varchar_ignorecase(50) not null not null );
И файл data.sql. В нем таблица заполняется данными:
insert into my_user(login, position, password, authority) values('user', '1', 'password', 'ROLE_USER'); insert into my_user(login, position, password, authority) values('admin', '2', 'password', 'ROLE_ADMIN');
Оба файла должны лежать в папке /resources.
Настройка
Аутентификация теперь настраивается так:
@Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery( "select login, password, 'true' from my_user " + "where login=?") .authoritiesByUsernameQuery( "select login, authority from my_user " + "where login=?"); }
Мы убрали схему по умолчанию и сделали два запроса получения пользователей и разрешений. Первый запрос получает:
username password enabled (включен ли пользователь)
Второй запрос получает:
username authority
Эти названия есть в стандартной схеме (см. выше). Из своей же схемы важно получить поля такого же типа, названия не важны. Поля enabled у нас вообще нет, так что выбираем константу true.
Вообще SQL-запросы могут быть совершенно любыми: из одной таблицы брать все значения (как у нас), из разных, использовать join и т.д. Главное указать два SQL-запроса, первый из которых выбирает три вышеприведенных поля, а второй два (чуть ниже) вышеприведенных поля.
Другая база данных
И последнее: поскольку в реальных проектах не используется H2, потребуется еще прописать настройки DataSource в файле application.yml. Например, такие:
spring: datasource: url: jdbc:postgresql://localhost/dbname username: postgres password: pass
Итоги
Пример доступен на GitHub.
а тут дополнительный пример с group_authorities