Используйте сеанс гибернации с выделенным подключением к источнику данных для каждого сеанса Spring Security

#java #spring #hibernate #session

#java #spring #спящий режим #сеанс

Вопрос:

Можно ли изменить аутентификацию базы данных a Hibernate Session (или создать новую) во время выполнения и связать ее с текущим зарегистрированным веб-пользователем?

Например. когда Spring Security пользователь, управляемый конкретным пользователем, входит в систему, повторно подключает его к другой роли базы данных и использует это соединение на протяжении всего срока службы http-сеанса для этого пользователя?

Комментарии:

1. Вы можете что-то делать, но это зависит от базы данных. Какая база данных?

2. Это PostgreSQL

Ответ №1:

Сеанс гибернации и http-сеанс не связаны, мне немного сложно понять ваш вопрос. Если вы должны были хранить данные пользователя, такие как роль, в сеансе http, у вас возникнет эта проблема, поскольку вы описываете, где роль была изменена, но у пользователя все еще есть старые привилегии, потому что Spring Security считывает данные пользователя из сеанса, а не из базы данных. Однако, если вы сохраняете только идентификатор пользователя и не сохраняете никакой информации о пользователе в сеансе http, у вас не возникнет проблем с обновлением. Обычно вы даже не сохраняете пользователя в сеансе, потому что вы можете просто получить доступ к принципалу весной. Сеансы гибернации создаются и закрываются за транзакцию. Вы должны использовать транзакции, чтобы база данных обрабатывала все проблемы параллелизма за вас, и вам не нужно беспокоиться о том, например, менялся ли пароль пользователя, в то время как какая-то другая транзакция собиралась его использовать.

Обычно вы не должны хранить ничего, что находится в базе данных, в сеансе Http. Это было то, что вам было нужно?

Редактировать Все еще немного неясно, о чем вы спрашиваете, у меня будет предположение. Вы хотите создать новые учетные данные базы данных для своей базы данных с привилегиями, как в mysql workbench или что-то в этом роде, а затем использовать подключение к базе данных с этими учетными данными для каждого сеанса Http при входе пользователя в систему?

В Spring нет механизма для этого, поскольку это будет зависеть от базы данных. Не рекомендуется создавать подключение к базе данных для каждого сеанса Http, так как вы потеряете свою приятную оптимизацию пула соединений, предоставляемую Hibernate и Spring. Если в вашем приложении выделено много пользователей, оно значительно снизит производительность от такого количества подключений, может даже просто зависнуть, если у вас было много пользователей. И снова мне нужно уточнить, что у вас может быть открытое соединение с базой данных, но вы не оставляете сеансы гибернации открытыми на длительные периоды, они просто оболочка для общения с подключением к базе данных, когда вам нужно. Хотя я не рекомендую вам это делать, и если бы вы были в моей команде, я думаю, есть 2 способа:

Во-первых, вам нужно будет создать учетные данные в командной строке через Java, вероятно, запустив какой-либо скрипт. Затем вам нужно будет создать источник данных сеанса и подключить его ко всем вашим Dao. Вы не сможете использовать какие-либо материалы Spring, такие как @Transactional или @PreAuthorize и т. Д. (хотя вы все равно можете использовать @PreAuthorize, просто не используйте hasRole или любые функции, которые будут обращаться к неправильному источнику данных). Вам просто нужно будет вручную выполнить логику разрешений и вручную запрограммировать сеансы гибернации.

Если этого для вас недостаточно, вы можете просто пойти полным ходом, приобрести чрезвычайно мощный сервер и запустить совершенно другое веб-приложение Spring «clone» для каждого сеанса http, которое является точно таким же, за исключением того, что у него другой корневой URL и новая конфигурация подключения. Затем перенаправьте этого пользователя на его личное веб-приложение, расскажите о сервисе 1-го класса.

Послушайте, честно говоря, это действительно плохая идея в любом случае, и вы можете буквально добиться того же пользовательского интерфейса, используя Spring Security. Подключение к базе данных — это просто ресурс, который нужно использовать оптимально. Я бы не стал предоставлять каждому пользователю веб-приложения собственное подключение, не больше, чем я бы дал каждому сотруднику в компании свой личный туалет. Вместо этого я бы дал им ключ-карту для разрешения совместного использования доступных туалетов, вам лучше это сделать.

Комментарии:

1. Я хочу подключение к базе данных с выделенным пользователем для каждого пользователя веб-приложения из той же учетной записи. Это понятнее?

2. Я понимаю, что нет связи между http и сеансом гибернации. Я хочу создать сеанс гибернации для каждого вошедшего в систему пользователя, который использует выделенного пользователя базы данных.

3. Вы становитесь ближе. Вместо подключения к базе данных для каждого сеанса http не было бы лучше и более целесообразно иметь Hibernate SessionFactory для каждого пользователя?

4. Вы можете создать любой компонент в своем приложении на основе сеанса, за исключением всех компонентов Spring, таких как диспетчер транзакций и менеджер безопасности. Вы все равно можете добиться динамического поведения с этими компонентами, используя либо сеансовые компоненты, либо данные, изменяющиеся в базе данных. Таким образом, вы могли бы создать SessionFactory с областью действия сеанса, но SessionFactory, который использует Spring Security, не сможет быть основан на сеансе, что не имеет логического смысла, это должно быть одноэлементным.

Ответ №2:

Я не уверен, что возможно иметь выделенный сеанс DB для каждого пользователя. Даже если это технически возможно, приложение с большой базой пользователей не будет масштабироваться очень хорошо. Альтернативный подход заключается в настройке стандартного пула соединений и связывании соединения с конкретным пользователем веб-приложения каждый раз, когда оно извлекается из пула.

Здесь есть некоторое обсуждение этого (с точки зрения Oracle, но те же принципы применяются независимо от СУБД):

Многие приложения используют объединение сеансов для настройки количества сеансов, которые будут повторно использоваться несколькими пользователями приложения. Пользователи аутентифицируются в приложении среднего уровня, которое использует единый идентификатор для входа в базу данных и поддерживает все пользовательские подключения. В этой модели пользователи приложения — это пользователи, которые прошли проверку подлинности на среднем уровне приложения, но которые неизвестны database…..in в таких ситуациях приложение обычно подключается как один пользователь базы данных, и все действия выполняются от имени этого пользователя. Поскольку все пользовательские сеансы создаются как один и тот же пользователь, эта модель безопасности очень затрудняет разделение данных для каждого пользователя. Эти приложения могут использовать атрибут CLIENT_IDENTIFIER для сохранения идентификатора пользователя реального приложения в базе данных.

https://docs.oracle.com/cd/B19306_01/network.102/b14266/apdvprxy.htm#i1010372

Как вы можете достичь этого, обсуждается в разделе 8.2 документации Spring ниже. Обратите внимание, что, хотя это скрыто в разделе, относящемся к расширениям Oracle Spring, в разделе 8.2 (в отличие от 8.1) нет ничего специфичного для Oracle (кроме выполняемого оператора), и общий подход должен быть осуществим с любой базой данных, просто указав соответствующий вызов процедуры или SQL:

http://docs.spring.io/spring-data/jdbc/docs/current/reference/html/orcl.connection.html

Я не слишком знаком с Postgres, но я предполагаю, что вызов, который вы хотели бы выполнить при каждой проверке соединения, будет примерно таким:

https://www.postgresql.org/docs/8.4/static/sql-set-role.html

В примере, приведенном в Spring docs, используется XML config. Если вы используете Java config, то это выглядит так:

 @Component
@Aspect
public class ClientIdentifierConnectionPreparer implements ConnectionPreparer
{
  @AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
  public Connection prepare(Connection connection) throws SQLException
  {
    SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String webAppUser = //;

    CallableStatement cs = connection.prepareCall("my postgres statement");
    cs.setString(1, webAppUser);
    cs.execute();
    cs.close();

    return connection;
  }
}

@Configuration
@EnableAspectJAutoProxy
public class SomeConfigurationClass
{

}