Настройка JDBC-аутентификации в Spring Security

Ранее мы рассмотрели 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). Ниже рассмотрим подробнее новую настройку.

В качестве PasswordEncoder выбираем, как обычно, NoOpPasswordEncoder, который ничего не шифрует, так как пример учебный, а с ним проще.

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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *