【问题标题】:Use a Hibernate Session with dedicated datasource connection per Spring Security session每个 Spring Security 会话使用具有专用数据源连接的 Hibernate 会话
【发布时间】:2017-01-28 01:15:29
【问题描述】:

是否可以在运行时更改Hibernate Session 的数据库身份验证(或创建一个新的),并将其链接到当前登录的网络用户?

例如当特定的Spring Security-managed 用户登录时,使用不同的数据库角色重新连接它,并在该用户的 http 会话的整个生命周期内使用该连接?

【问题讨论】:

  • 你可以做一些事情,但它依赖于数据库。哪个数据库?
  • 这是 PostgreSQL

标签: java spring hibernate session


【解决方案1】:

休眠会话和 http 会话不相关,我很难理解您的问题。如果您要在 http 会话中存储用户详细信息(例如角色),当您描述角色已更改的位置但用户仍然拥有旧权限时,您将遇到此问题,因为 Spring Security 正在从会话中读取用户详细信息,而不是数据库。但是,如果您只存储用户 ID,而不在 http 会话中保留有关用户的任何信息,则不会出现刷新问题。通常您甚至不会将用户存储在会话中,因为您可以在 Spring 中访问主体。 每个事务都会创建和关闭 Hibernate 会话。您应该使用事务,以便数据库为您处理所有并发问题,并且您不必担心例如用户密码被更改而其他事务将使用它。

通常,您不应在 Http 会话中存储数据库中的任何内容。这就是你所追求的吗?

编辑 还是有点不清楚你在问什么,我猜一下。您是否想为您的数据库创建新的数据库凭据,具有您在 mysql 工作台或其他东西中的权限,然后在用户登录时为每个 Http 会话使用具有这些凭据的数据库连接?

Spring 中没有这样做的机制,因为这将依赖于数据库。不建议为每个 Http 会话创建一个数据库连接,因为您将失去 Hibernate 和 Spring 为您提供的漂亮的小连接池优化。如果您的应用程序有很多用户,那么它会因连接过多而严重影响性能,如果您有很多用户,甚至可能会冻结。我需要再次澄清您可能与数据库建立了开放连接,但是您不会长时间打开休眠会话,它们只是在需要时与数据库连接对话的包装器。虽然我不建议你这样做,如果你在我的团队中,我会用橄榄球来阻止你,但我想有两种方法:

首先,您可能必须通过运行一些脚本的 Java 在命令行上创建凭据。然后你需要创建一个会话数据源,并将它连接到你所有的 Daos。您将无法使用任何 Spring 的东西,例如 @Transactional 或 @PreAuthorize 等。 (虽然你仍然可以使用@PreAuthorize,只是不要使用hasRole,或者任何会与错误数据源对话的函数)。您只需手动执行权限逻辑,并手动编写您的休眠会话。

如果这对你来说还不够好,你可以完全疯狂,购买一个非常强大的服务器,然后为每个 http 会话启动一个完全不同的“克隆”Spring Webapp,除了它有不同的根 URL 和新的连接配置。然后将该用户重定向到他们的个人 web 应用程序,谈论 1st class 服务。

老实说,无论哪种方式,这都是一个非常糟糕的主意,并且您可以通过使用 Spring Security 来实现相同的用户体验。数据库连接只是一种可以优化使用的资源。我不会给 webapp 的每个用户自己的连接,就像我不会给公司的每个员工自己的个人厕所一样。相反,我会给他们一张钥匙卡,以允许他们共享可用的厕所,你应该这样做。

【讨论】:

  • 我想为来自同一帐户的每个 Web 应用程序用户建立一个与专用用户的数据库连接。是不是更清楚了?
  • 我知道http和hibernate会话之间没有链接。我想为每个登录用户创建一个休眠会话,它使用一个专用的数据库用户。
  • 你越来越近了。代替每个 http 会话的数据库连接,每个用户都有一个 Hibernate SessionFactory 不是更好更可行吗?
  • 您可以在您的应用程序会话中创建任何组件,除了事务管理器和安全管理器等所有 Spring 组件。您仍然可以通过会话 bean 或数据库中的数据更改这些组件来实现动态行为。因此,您可以创建一些会话范围的 sessionFactory,但 Spring Security 使用的 sessionFactory 不能基于会话,这没有逻辑意义,需要单例。
【解决方案2】:

我不确定为每个用户设置一个专用数据库会话是否可行。即使在技术上可行,拥有大量用户群的应用程序也不会很好地扩展。另一种方法是配置标准连接池,并在每次从池中签出连接时将连接与特定的 Web 应用程序用户相关联。

这里有一些讨论(从 Oracle 的角度来看,但无论 RDBMS 是什么,相同的原则都适用):

许多应用程序使用会话池来设置多个会话 供多个应用程序用户重用。用户认证 他们自己到一个中间层应用程序,它使用一个单一的身份 登录到数据库并维护所有用户连接。在 在此模型中,应用程序用户是经过身份验证的用户 应用程序的中间层,但数据库不知道谁......在 在这些情况下,应用程序通常连接为单个 数据库用户和所有操作都作为该用户执行。因为所有用户 会话是作为同一个用户创建的,这种安全模型使它 很难为每个用户实现数据分离。 这些 应用程序可以使用 CLIENT_IDENTIFIER 属性来保存 真正的应用程序用户身份通过​​数据库。

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

下面的 Spring 文档第 8.2 节讨论了如何实现这一点。请注意,虽然这隐藏在特定于 Spring 的 Oracle 扩展的部分中,但第 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 文档中给出的示例使用 XML 配置。如果您使用的是 Java 配置,那么它看起来像:

@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
{

}

【讨论】:

    猜你喜欢
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    相关资源
    最近更新 更多