副标题#e#
引言
在一些对安详审计有较高要求的系统中,打点员需要查察每个应用措施的登任命户执行了哪些数据库操纵,而凡是我们应用措施在会见数据库时,都是公用同一个数据库的认证用户去获取数据库毗连的,这样我们的登任命户的标识无法通报到数据库端。而许多客户是需要在数据库端能审计登任命户的操纵。虽然,在应用处事器端的措施里写日志,记录下每个登任命户执行了哪些操纵也能到达审计的需求,但这种方法往往会有机能的开销。颠末实践,本文先容的办理途径是将用户标识通过数据库毗连通报到数据库端,从而完成在数据库端的审计,这是一种较轻量级的方法。
办理方案简述
在 JDBC 4.0 之前,JDBC 类型没有提供通报用户标识的 API,我们只能通过数据库厂商提供的 API 去实现。思量到这一需求的实用性,JDBC 4.0 为我们增加了相应的 API。今朝,Oracle 11g Release 1 (11.1) 和 DB2 9.5 的 JDBC driver 都支持 JDBC 4.0 类型,但在此之前的版本中,我们只能借助于厂商提供的 API。本文先容了利用这些 API 的一些实践,同时说明白如安在数据库端查察通报过来的用户标识。
通报用户标识的根基模式为:
数据库会话(session)
JDBC framework(或 O/R mapping 框架)如 Hibernate 和 iBatis 都提供了 session 的观念,session 是对数据库毗连和事务利用的封装。一个数据库会话期间凡是利用一个毗连,对应一个事务。
对付纯 JDBC 来说,得到一个数据库毗连就相当于开启了一个会话。
打开一个数据库会话。
配置用户标识信息。
执行一些数据库操纵。
排除毗连上的用户标识。
封锁数据库会话。
个中,排除毗连上的标识很是重要,因为我们凡是利用的数据库毗连都是逻辑毗连,封锁逻辑毗连后其对应的物理毗连 (TCP/IP 毗连 ) 并未封锁,所以排除毗连上的标识信息可以确保不影响此外数据库逻辑毗连。
JDBC 4.0 提供的支持
Java 6 支持 JDBC 4.0 类型,在 JDBC 4.0 中提供了在数据库毗连 java.sql.Connection 上通报用户信息的支持。在该接口中提供了两个要领:
void setClientInfo(String name, String value) throws SQLClientInfoException;
void setClientInfo(Properties properties) throws SQLClientInfoException;
第一个要领答允我们在 Connection 上通报三个属性:
ApplicationName:会见数据库的应用措施名称。
ClientUser: 会见数据库的用户标识,这个用户和成立数据库毗连的用户是差异的。成立数据库毗连的用户是被能数据库认证和被授权过的用户。
ClientHostname:会见数据库客户端的主机名。
第二个要领和第一个要领成果雷同,只是将参数放到了一个 Properties 工具中。我们凡是 setClientInfo(“ClientUser” , userId) 将用户标识附加在数据库毗连上。利用该要领的常见模式是:
清单 1. 利用 JDBC 4.0 API 通报用户标识
Connection conn = getConnection();
conn.setClientInfo("ClientUser" , currentUserId);
//do something on the connection
conn.setClientInfo("ClientUser" , null);
conn.close();
留意,排除毗连上的用户标识的方法是将标识置为空。下面我们针对两种的常用数据库范例先容标识通报的要领。
针对 DB2 的标识通报
DB2 提供了 com.ibm.db2.jcc.DB2Connection,该类有下列要领,支持用户信息通报:
public void setDB2ClientUser(String s) throws SQLException;
public void setDB2ClientWorkstation(String s) throws SQLException;
public void setDB2ClientApplicationInformation(String s) throws SQLException;
public void setDB2ClientAccountingInformation(String s) throws SQLException;
#p#副标题#e#
在得到毗连后,通过上面的要领在毗连上配置用户信息,在利用完毕后通过置空来排除毗连上的用户信息。示例代码如下:
清单 2. 利用 DB2Connection 通报用户标识
DB2Connection conn ;
DriverManager.registerDriver(new com.ibm.db2.jcc.DB2Driver());
String connString = "jdbc:db2://hostname:50000/dbname" ;
conn = (DB2Connection)DriverManager.getConnection(connString, "connUser", "connPasswd");
// 上面的毗连也可以从 DataSource 上获取
conn.setDB2ClientUser(“loginUser”) ;
//do something on the connection
conn.setDB2ClientUser(null) ;
conn.close() ;
在开放式平台上,通过下面的 DB2 呼吁来查察通报过来的用户信息:db2 get snapshot for applications on databasealias,输出功效示例:
#p#分页标题#e#
TP Monitor client user ID = DB2UserID
TP Monitor client workstation name = yourApplication
TP Monitor client application name = clientWorkstation
TP Monitor client accounting string = yourAccountingInfo
在主机(z/OS)上,通过 DB2 呼吁: -DISPLAY THREAD(*) DETAIL 来查察,输出功效示例:
DSNV401I -DB8G DISPLAY THREAD REPORT FOLLOWS - DSNV402I -DB8G ACTIVE THREADS
-NAME ST A REQ ID AUTHID PLAN ASID TOKEN SERVER RA * 4 V2.27.1302 DB2USER DISTSERV
0042 17 V437-WORKSTATION=clientWorkstation, USERID=DB2UserID,
APPLICATION NAME=yourApplication
针对 Oracle 的标识通报
在 Oracle 11g Release 1 之前的版本中,Oracle JDBC driver 提供了接口 oracle.jdbc.driver.OracleConnection,通过 OracleConnection 上的两个要领 setClientIdentifier() 和 clearClientIdentifier() 可以完成标识通报。OracleConnection 只能通报一个属性 clientIdentifier,但凡是这已经足够。
示譬喻下:
清单 3. 利用 OracleConnection 通报用户标识
OracleDataSource dataSource = new OracleDataSource();
dataSource.setURL("jdbc:oracle:thin:@hostname:1521:orcl");
dataSource.setUser("username");
dataSource.setPassword("passwd");
conn = (OracleConnection) dataSource.getConnection();
conn.setClientIdentifier(clientId) ;
// do something on the connection
conn.clearClientIdentifier(clientId) ;
conn.close() ;
dataSource.close() ;
这个 client_id 传到 oracle 后,可以通过下面 sql 语句来查察每个 session 上的用户标识。
select client_identifier from v$session
那如何看到每个 client_id 执行的 sql 呢?需打开 oracle 的审计开关。譬喻可以打开对查询语句的审计:
audit select table by session;
然后执行:
select sql_text,CLIENT_ID from dba_audit_trail where username='connectionUser'
order by EXTENDED_TIMESTAMP desc
可以列出每个用户执行的 sql 语句。
数据源在 WebSphere 应用处事器上的景象
假如是回收 WebSphere 应用处事器上设置的数据源,则无法将数据源上得到的毗连转化为 OracleConnection 或 DB2Connection,须回收 WAS 提供的 connection wrapper 类 com.ibm.websphere.rsadapter.WSConnection。编程模子如下:
清单 4. 利用 WSConnection 通报用户标识
import com.ibm.websphere.rsadapter.WSConnection;
…
InitialContext ctx = new InitialContext();
DataSource ds = (javax.sql.DataSource) ctx.lookup("jbdc/mydatasource") ;
conn = ds.getConnection();
WSConnection wsconn = (WSConnection) conn ;
Properties props = new Properties();
props.setProperty(WSConnection.CLIENT_ID, clientId);
wsconn.setClientInformation(props);
//do something on the wsconn
wsconn.setClientInformation(null); // 排除毗连上的用户信息
WSConnection 支持下列属性的通报:
WSConnection.CLIENT_ACCOUNTING_INFO
WSConnection.CLIENT_LOCATION
WSConnection.CLIENT_ID
WSConnection.CLIENT_APPLICATION_NAME
WSConnection.CLIENT_OTHER_INFO
WSConnection.OTHER_CLIENT_TYPE
和开源项目标团结
在实际大型项目中,直接通过 JDBC API 会见数据库较量少见,大多通过 O/R mapping 框架如 iBatis 或 Hibernate 去哄骗数据库。这些框架往往对数据库毗连举办了封装,同时客户的框架又常常举办了二次封装,这使得在毗连上通报属性变得不太容易。下面针对 iBatis 和 Hibernate 提出了本身的一些实践解法。
下面都是针对 JDBC 4.0 之前的 JDBC driver 的编程实践。
在 iBatis 中通报毗连属性
iBatis 提供了一个接口 com.ibatis.sqlmap.client.SqlMapClient,这个接口包括了数据库增删改查的常用要领。许多客户都是基于该接口的一个 wrapper 类去完成数据库操纵。但 SqlMapClient 默认的要领封装掉了对毗连的利用,即开拓者无须得到毗连和释放毗连即可利用。
客户常用的 SqlMapClient 包装类的形式:
清单 5. 一个典范的 SqlMapClient 封装类
public class SqlMapClientUtil {
private SqlMapClient sqlMap ;
public SqlMapClientUtil(SqlMapClient sqlMap) {
this.sqlMap = sqlMap ;
}
public SqlMapClient getSqlMap() {
return sqlMap ;
}
…
}
#p#分页标题#e#
客户利用这种包装类的长处是减轻挪用方对 SqlMapClient 的初始化事情,同时也可以对 SqlMapClient 做一些加强。但假如需要在毗连上通报属性,需要举办一些改革。改革步伐是写一个本身的 SqlMapClient 实现,逐一实现 SqlMapClient 里的要领。
清单 6. 一个自定制的 SqlMapClient 实现
public class MySqlMapClient implements SqlMapClient{
SqlMapClient sqlMap ;
public MySqlMapClient(SqlMapClient sqlMap) {
this.sqlMap = sqlMap ;
}
public Object insert(String id, Object parameterObject) throws SQLException {
Object retObj = null ;
OracleDataSource dataSource = null ;
OracleConnection conn = null ;
try {
conn = (OracleConnection)dataSource.getConnection();
SqlMapSession session = sqlMap.openSession(conn);
conn.setClientIdentifier("") ;
sqlMap.setUserConnection(conn) ;
retObj = session.insert(id, parameterObject) ;
conn.clearClientIdentifier("") ;
conn.commit() ;
} catch (Exception e) {
// TODO: handle exception
} finally {
try {
conn.close() ;
} catch (Exception e2) {
// TODO: handle exception
}
}
return retObj ;
}
//other methods … .
}
于是将上面的 SqlMapClientUtil 重组成:
清单 7. 重构过的 SqlMapClient 封装类
public class SqlMapClientUtil {
private SqlMapClient sqlMap ;
public SqlMapClient getSqlMapClient() {
return new MySqlMapClient(sqlMap) ;
}
…
}
在 Hibernate 中通报毗连属性
典范的利用 Hibernate 操纵数据库的编程模子如下:
清单 8. Hibernate 的典范编程模子
Session sess = factory.openSession();
Transaction tx;
try {
tx = sess.beginTransaction();
//do some work
...
tx.commit();
} catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
} finally {
sess.close();
}
为了能在会话内通报用户标识,将上述编程模子改革成下面方法即可:
清单 9. 加进通报用户标识后的 Hibernate 编程模子
Session sess = factory.openSession();
Transaction tx;
Connection conn ;
try {
tx = sess.beginTransaction() ;
conn = sess.connection() ;
OracleConnection oraconn = (OracleConnection)conn ;
// 上面毗连或转换成 DB2Connection,视数据库而定
oraconn.setClientIdentifier("") ;
//do some work
oraconn.clearClientIdentifier("") ;
tx.commit();
} catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
} finally {
sess.close();
}
竣事语
本文源于客户的真实场景,许多客户在实际 Java EE 项目中都有“在数据库端审计前端登任命户”的需求。本文针对几种典范场景给出了如何通报用户标识的编程实践,并先容了如安在数据库端举办审计查察。但愿能给相关开拓者提供一些警惕。