Java 持久化/审计和安全
JPA 通常用于具有共享连接池的中层/服务器环境。连接池允许共享数据库连接以避免重新连接到数据库的成本。通常,用户登录到应用程序,但没有他们自己的数据库登录,因为共享数据库登录用于连接池。这与传统的两层应用程序模型不同,在传统的两层应用程序模型中,每个用户都有他们自己的数据库登录。大多数数据库提供审计支持来记录更改并提供基于用户的安全。但是,在具有共享连接池和 Web 用户的三层环境中,这通常不起作用。
有几种审计和安全解决方案
- 为每个应用程序用户提供一个数据库用户 ID,并为每个用户提供他们自己的数据库连接。
- 使用公共数据库用户 ID,并在应用程序中管理审计和安全。
- 使用数据库代理身份验证来允许共享连接池和用户上下文。
这允许进行基于数据库用户的审计和安全。如果每个应用程序用户都有他们自己的数据库用户 ID,则连接不能共享。每个用户在创建他们的 EntityManager 时都需要创建一个新的数据库连接。
JPA 没有关于如何执行此操作的标准,但一些 JPA 提供商允许将 JPA 持久性单元属性作为 EntityManager
属性传递以配置 EntityManager
连接。如果您的 JPA 提供商不支持此功能,那么您可以尝试为每个用户创建 EntityManagerFactory
以允许传递连接属性。但这可能很昂贵,因此请谨慎操作,确保禁用共享缓存并将连接池大小设置为 1。
- EclipseLink/TopLink : 通过允许将 JPA 持久性单元属性传递给 EntityManagerFactory.createEntityManager(Map) API 来支持这种模型。应用程序可以传递“javax.persistence.jdbc.user”和“javax.persistence.jdbc.password”属性以触发为此 EntityManager 创建新的连接。请注意,此连接默认情况下仅用于写入,读取仍将使用共享连接池。要强制读取也使用连接,应将“eclipselink.jdbc.exclusive-connection.mode”属性设置为“Always”,但这取决于应用程序是否希望审计写入或读取。EclipseLink 还定义了一个“eclipselink.jdbc.exclusive-connection.is-lazy”属性,用于配置连接是否应立即连接,或者仅在第一次需要时连接。如果只审计写入,那么延迟连接允许避免创建新数据库连接的成本,除非发生写入。
Map properties = new HashMap();
properties.put("javax.persistence.jdbc.user", user);
properties.put("javax.persistence.jdbc.password", password);
EntityManager em = factory.createEntityManager(properties);
如果使用 Java EE 和 DataSource,则数据库用户名和密码可能能够以相同的方式传递,具体取决于您的 JPA 提供商。
如果使用 JEE 和 JTA 管理的 EntityManager
,则指定用户/密码可能更困难,因为 EntityManager 和 JDBC 连接不在应用程序的控制之下。持久性单元属性仍然可能能够在 EntityManager
上指定。只要在 EntityManager
建立数据库连接之前完成此操作,它仍然会起作用。
在 JPA 2.0 中,可以使用 setProperty API
em.setProperty("javax.persistence.jdbc.user", user);
em.setProperty("javax.persistence.jdbc.password", password);
通常,每个用户不会分配不同的安全权限,而是定义一组分配了安全权限的角色,并将用户分配到角色中。如果为每个角色创建一个应用程序数据库用户,那么您就可以拥有多个安全级别,但仍然允许连接池。
在 JPA 中启用此功能的一种方法是为每个角色定义不同的连接池和持久性单元。这将允许连接池并处理基于角色的数据库安全,但它在角色之间共享缓存方面存在问题。
一些 JPA 提供商可能允许单个持久性单元定义多个连接池。
审计通常通过具有应用程序用户和单个共享数据库用户来在应用程序中管理。这通常通过在所有已审计表中添加 AUDIT_USER
和 AUDIT_TIMESTAMP
列,以及在所有已审计对象中添加 auditUser
和 auditTimestamp
字段来实现。当应用程序插入或更新对象时,它将设置这些字段,并将它们存储在数据库中。JPA 事件也可以用于记录审计信息,或写入单独的审计表。
安全也由应用程序控制,允许用户根据他们的应用程序用户 ID 或角色访问应用程序的不同部分。数据库可用于存储用户登录信息、角色和访问权限,但这些信息仅位于普通表中,与数据库用户无关,并且数据库不会强制执行其自身的安全性。
这种模型允许完全连接池,并使应用程序能够控制审计和安全。
另请参阅:
- 历史.
import javax.persistence.*;
@MappedSuperclass
public Class AuditedObject {
public static ThreadLocal currentUser = new ThreadLocal();
@Column("AUDIT_USER");
protected String auditUser;
@Column("AUDIT_TIMESTAMP");
protected Calendar auditTimestamp;
public String getAuditUser() {
return auditUser;
}
public void setAuditUser(String auditUser) {
this.auditUser = auditUser;
}
public Calendar getAuditTimestamp() {
return auditTimestamp;
}
public void setAuditTimestamp(Calendar auditTimestamp) {
this.auditTimestamp= auditTimestamp;
}
@PrePersist
@PreUpdate
public void updateAuditInfo() {
setAuditUser((String)AuditedObject.currentUser.get());
setAuditTimestamp(Calendar.getInstance());
}
}
某些数据库(如 Oracle 数据库)提供了一种机制,可以在现有数据库连接上设置代理用户。这允许使用共享连接池,但也为数据库提供了一个用户上下文。
一些 JPA 提供商支持代理身份验证。
- EclipseLink/TopLink : 支持 Oracle 代理身份验证。
某些数据库(如 Oracle 数据库)支持行级安全(虚拟专用数据库)。典型的数据库安全只允许按表分配访问权限。行级安全允许不同的用户访问每个表中的不同行。
一些 JPA 提供商支持行级安全和 VPD。
- EclipseLink/TopLink : 提供对 Oracle VPD 的支持。