Ранее мы рассмотрели 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