Для приложения Taco Cloud вы хотите, чтобы клиенты могли регистрироваться в приложении и управлять своими учетными записями пользователей. Это не соответствует ограничениям хранилища пользователей в памяти, поэтому давайте рассмотрим другой вариант, который позволяет использовать хранилище пользователей, поддерживаемое базой данных.
4.2.2 Хранилище пользователей на основе JDBC
Сведения о пользователях часто хранятся в реляционной базе данных, и хранилище пользователей на основе JDBC кажется подходящим. В следующем списке показано, как настроить Spring Security для проверки подлинности сведений о пользователях, хранящихся в реляционной базе данных с помощью JDBC.
Листинг 4.3 Идентификация через JDBC-базу
@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource);
}
Эта реализация configure() вызывает jdbcAuthentication() на данном AuthenticationManagerBuilder. Для этого необходимо установить DataSource, чтобы он знал, как получить доступ к DataSource. DataSource, используемый здесь, обеспечивается магией autowiring.
ПЕРЕОПРЕДЕЛЕНИЕ ПОЛЬЗОВАТЕЛЬСКИХ ЗАПРОСОВ ПО УМОЛЧАНИЮ
Хотя эта минимальная конфигурация будет работать, она делает некоторые предположения о схеме базы данных. Предполагается, что существуют определенные таблицы, в которых будут храниться пользовательские данные. В частности, следующий фрагмент кода из внутренних компонентов Spring Security показывает SQL-запросы, которые будут выполняться при поиске сведений о пользователе:
public static final String DEF_USERS_BY_USERNAME_QUERY =
"select username,password,enabled " +
"from users " +
"where username = ?";
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
"select username,authority " +
"from authorities " +
"where username = ?";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
"select g.id, g.group_name, ga.authority " +
"from groups g, group_members gm, group_authorities ga " +
"where gm.username = ? " +
"and g.id = ga.group_id " +
"and g.id = gm.group_id";
Первый запрос извлекает имя пользователя, пароль и информацию о том, включены они или нет. Эта информация используется для аутентификации пользователя. Следующий запрос ищет предоставленные полномочия пользователя для целей авторизации, а последний запрос ищет полномочия, предоставленные пользователю как члену группы.
Если вы согласны с определением и заполнением таблиц в базе данных, удовлетворяющих этим запросам, вам больше нечего делать. Но, скорее всего, ваша база данных не выглядит так, и вам понадобится больше контроля над запросами. В этом случае можно настроить собственные запросы.
Листинг 4.4 Настройка запросов сведений о пользователе
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username, password, enabled from Users " +
"where username=?")
.authoritiesByUsernameQuery(
"select username, authority from UserAuthorities " +
"where username=?");
}
В этом случае переопределяются только запросы проверки подлинности и обычной авторизации. Но можно также переопределить запрос полномочий группы, вызвав groupAuthoritiesByUsername () с помощью пользовательского запроса.
При замене SQL-запросов по умолчанию, запросами собственной разработки важно придерживаться базового контракта запросов. Все они принимают имя пользователя в качестве единственного параметра. Запрос проверки подлинности выбирает username, password и enabled. Запрос привилегий выбирает ноль или более строк, содержащих username и authority. Запрос привилегий группы выбирает ноль или более строк, каждая с идентификатором группы, group_name и authority.
РАБОТА С ЗАКОДИРОВАННЫМИ ПАРОЛЯМИ
Сосредоточившись на запросе проверки подлинности, можно увидеть, что пароли пользователей должны храниться в базе данных. Единственная проблема с этим заключается в том, что если пароли хранятся в текстовом виде, они могут стать доступны для любопытных глаз хакеров. Но если вы закодируете пароли в базе данных, проверка подлинности завершится ошибкой, так как она не будет соответствовать паролю открытого текста, отправленному пользователем.
Для решения этой проблемы, вам нужно указать кодировщик пароля, обратившись к методу passwordEncoder():
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username, password, enabled from Users " +
"where username=?")