審計字段即記錄數據的建立人、建立時間、修改人、修改時間的字段、體如今每一張數據庫表中。爲了減小代碼量須要設置一套通用的方法。 java
思路:登陸用戶存入session,訪問數據庫的時候攔截器獲取連接先設入數據庫session,隨後進行業務邏輯,每一個表設置trigger,每次更新時獲取session中的用戶名稱,設置入審計字段。 web
一、用戶信息Web到App的傳遞。 sql
思路:攔截分發器,每次分發請求的時候在ServiceRequest中設置用戶信息傳遞到App。 數據庫
首先設置頁面攔截,獲取session中的用戶信息放入用戶線程上下文。 session
web-context.xml app
<bean id="sessionInterceptor" class="com.palic.egis.common.web.util.SessionInterceptor"> <description>AuthorizationController用來檢查用戶是否login</description> <property name="worktableAssociatedFilter"> <list> <value>/index.screen</value> </list> </property> </bean> <bean id="defaultHandlerMapping" class="com.paic.pafa.app.web.servlet.handler.BeanNameUrlHandlerMapping"> <description> 當一個HTTP請求進來的時候,interceptors先攔截請求,進行預先處理。 </description> <property name="interceptors"> <list> <ref local="sessionInterceptor"/> </list> </property> </bean>
common-context.xml less
<!--=====================================================================--> <!-- 線程context的配置 --> <!--=====================================================================--> <bean id="userThreadContext" class="com.paic.pafa.core.service.PafaThreadContext"> <description>線程的Context</description> </bean>
SessionInterceptor.java 攔截器實現 函數
/** * 用於檢查Session裏是有用戶登陸信息的攔截器。Interceptor的功能相似於Servlet的Filter。 * 當一個HTTP請求進來的時候,interceptors先攔截請求,進行預先處理。 * * @author Leo Liao, 2005-4-14, created * @version $Revision$Date$ * @see com.palic.egis.support.privilege.web.controller.LoginController */ public class SessionInterceptor extends HandlerInterceptorAdapter { // 用於獲取用戶信息的LoginController private Controller authorizationController; // 目前SessionAdmin有待完善,請暫時不要使用 // private SessionAdmin sessionAdmin; /** * 檢查session裏是否是有用戶信息 * * @see com.palic.egis.support.privilege.web.controller.LoginController */ private boolean checkSession(HttpServletRequest request) { HttpSession session = request.getSession(); if (session != null && session.getAttribute("currentUser") != null) { return true; } return false; } public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws PafaWebException { try { if (!checkSession(request)) { authorizationController.handleRequest(request, response); if (!checkSession(request)) { throw new PafaWebException("appdemo.error.session.invalid"); } } // begin 設置當前用戶到線程上下文 HttpSession session = request.getSession(); String uid = (String) session.getAttribute("currentUser"); PafaThreadContext tc = (PafaThreadContext) PafaAppCommonContexton .getInstance() .getBean(SystemObjectName.USER_THREAD_CONTEXT); tc.putTxnID(PafaCoreContexton.getInstance().getIDGenerator() .getID()); tc.putUserID(uid); // end } catch (PafaWebException ex) { // throw new PafaWebException("appdemo.error.session.invalid", ex); request.getSession().setAttribute("loginError", ex.getInitialCause().getMessage()); } catch (Exception ex) { // throw new PafaWebException("appdemo.error.session.invalid", ex); request.getSession().setAttribute("loginError", ex.getMessage()); } return true; } /** * 清除threadContext中設置的用戶信息 */ public final void afterCompletion(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, Exception exception) throws Exception { PafaThreadContext tc = (PafaThreadContext) PafaAppCommonContexton .getInstance().getBean(SystemObjectName.USER_THREAD_CONTEXT); tc.clear(); super.afterCompletion(httpservletrequest, httpservletresponse, obj, exception); } }
common-context.xml 分發器配置 fetch
<!-- Real pafaAC --> <bean id="pafaACTarget" class="com.paic.pafa.app.lwc.service.remoting.access.ejb.SmartRemoteStatelessSessionProxyFactoryBean"> <property name="jndiName"> <value>ejb/egis/PafaAC</value> </property> <property name="businessInterface"> <value> com.paic.pafa.app.biz.ac.ApplicationController </value> </property> <property name="jndiTemplate"> <ref local="pafaACJndi" /> </property> </bean> <!-- pafaAC Proxy --> <bean id="pafaAC" class="com.paic.pafa.app.lwc.core.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="pafaACTarget"/> </property> <property name="proxyInterfaces"> <value>com.paic.pafa.app.biz.ac.ApplicationController</value> </property> <property name="interceptorNames"> <list> <value>dispatchServiceAdvisor</value> </list> </property> </bean> <bean id="dispatchServiceAdvisor" class="com.paic.pafa.app.lwc.core.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName"> <value>handleRequest</value> </property> <property name="advice"> <ref bean="dispatchServiceInterceptor"/> </property> </bean> <bean id="dispatchServiceInterceptor" class="com.palic.egis.common.web.util.ThreadContextInterceptor"> <property name="threadContext"> <ref bean="userThreadContext"/> </property> </bean>
ThreadContextInterceptor.java ui
public class ThreadContextInterceptor implements MethodInterceptor { private PafaThreadContext threadContext = null; public PafaThreadContext getThreadContext() { return threadContext; } public void setThreadContext(PafaThreadContext threadContext) { this.threadContext = threadContext; } public Object invoke(MethodInvocation method) throws Throwable { Object[] args = method.getArguments(); ServiceRequest sr = (ServiceRequest) args[0]; if (sr != null) { SessionDTO dto = sr.getSessionDTO(); if(dto.getUserId() == null){ dto.setUserId(threadContext.getUserID()); } if (dto.getTxnId() == null) { dto.setTxnId(threadContext.getTxnID()); } } return method.proceed(); }
思路:攔截數據源,先設置數據庫session在執行業務邏輯
biz-context.xml
<bean id="dsFactory" class="com.paic.pafa.app.lwc.service.persistence.datasource.DataSourceFactoryBean" > <property name="defaultDSKey"> <description>缺省的數據源,必須指定爲下面map中的entry key之一</description> <value>GBSDS</value> </property> <property name="dataSources"> <description>能夠在map屬性裏面添加多個數據源</description> <map> <entry key="GBSDS"> <ref local="defaultDS"/> </entry> <entry key="GBSDS_XA"> <ref local="gbsDS_XA"/> </entry> </map> </property> </bean> <bean id="defaultDS" class="com.paic.pafa.app.lwc.core.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="defaultDSTarget" /> </property> <property name="proxyInterfaces"> <value>javax.sql.DataSource</value> </property> <property name="interceptorNames"> <list> <value>dbConnectionAdvisor</value> </list> </property> </bean> <bean id="dbConnectionAdvisor" class="com.paic.pafa.app.lwc.core.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName"> <value>getConnection</value> </property> <property name="advice"> <ref bean="dbConnectionInterceptor"/> </property> </bean> <bean id="dbConnectionInterceptor" class="com.palic.egis.common.util.SetLcuInterceptor"> </bean> <bean id="defaultDSTarget" class="com.paic.pafa.app.lwc.core.naming.JndiObjectFactoryBean"> <property name="jndiName"> <value>${defaultDS}</value> </property> <property name="jndiTemplate"> <ref local="jndiTemplate"/> </property> </bean> <bean id="gbsDS_XA" class="com.paic.pafa.app.lwc.core.naming.JndiObjectFactoryBean"> <property name="jndiName"> <value>${GBSDS_XA}</value> </property> <property name="jndiTemplate"> <ref local="jndiTemplate"/> </property> </bean>
public class SetLcuInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation mi) throws Throwable { String userId = PafaCoreContexton.getInstance().getThreadContext().getUserID(); Connection conn = (Connection) mi.proceed(); if (userId != null && !userId.startsWith("V_") && !userId.equalsIgnoreCase("GMONIUSER")) { try { PreparedStatement stat = null; // 設置LCU try { stat = conn.prepareStatement("{call pub_sys_package.set_attributes(?)}"); stat.setString(1, userId); stat.execute(); } catch (Throwable e) { throw e; } finally { if (stat != null) { stat.close(); } } } catch (Throwable e) { DevLog.error("SetLcuInterceptor Error:" + e.getMessage()); } } return conn; } }
CREATE OR REPLACE PACKAGE BODY pub_sys_package IS --get user的兩種訪問1/pro PROCEDURE get_user_p(p_user OUT VARCHAR2) IS BEGIN p_user := get_user; END get_user_p; --get user的兩種訪問2/fun FUNCTION get_user RETURN VARCHAR2 IS v_user VARCHAR2(100); CURSOR cur_empno IS SELECT user_empno FROM gbs_user --根據登陸名稱查詢 WHERE user_name = USER; BEGIN SELECT sys_context('user_policy_context', 'uid') INTO v_user FROM dual; IF v_user IS NULL THEN OPEN cur_empno; FETCH cur_empno INTO v_user; CLOSE cur_empno; END IF; IF v_user IS NULL THEN v_user := USER; END IF; RETURN v_user; END get_user; --*********************************************** -- 功能說明: -- 在得到Connection時調用,用於將用戶信息設置到數據庫的context中 -- 參數說明: -- uid 用戶標誌 -- 調用函數: -- 無 --*********************************************** PROCEDURE set_attributes(uid VARCHAR2) IS regionlist VARCHAR2(100) := NULL; BEGIN dbms_session.set_context('user_policy_context', 'logon', 'true'); dbms_session.set_context('user_policy_context', 'uid', uid); END set_attributes; --*********************************************** -- 功能說明: -- 在得到Connection時調用,用於將用戶信息設置到數據庫的context中。egis-pos專用 -- 在lcu長度不夠的時代,保全利用該過程傳入full_uid,記錄在另外的字段中 -- 參數說明: -- uid 用戶標誌 -- 調用函數: -- 無 --*********************************************** PROCEDURE set_attributes(uid VARCHAR2, full_uid VARCHAR2) IS regionlist VARCHAR2(100) := NULL; BEGIN dbms_session.set_context('user_policy_context', 'logon', 'true'); dbms_session.set_context('user_policy_context', 'uid', uid); dbms_session.set_context('user_policy_context', 'full_uid', full_uid); END set_attributes; --*********************************************** -- 功能說明: -- 數據庫集中:將從UM中獲取的region信息set到context中 -- 參數說明: -- uid 用戶名 -- access_region 可訪問的region列表,以,分隔 -- 調用函數: -- 無 --*********************************************** PROCEDURE set_access_attributes(uid VARCHAR2, access_region VARCHAR2) IS BEGIN dbms_session.set_context('user_policy_context', 'logon', 'true'); dbms_session.set_context('user_policy_context', 'uid', uid); dbms_session.set_context('user_policy_context', 'access_region', access_region); END set_access_attributes; FUNCTION get_fcu(infcu IN VARCHAR2) RETURN VARCHAR2 IS v_fcu VARCHAR2(100); v_user_empno VARCHAR2(100); BEGIN v_user_empno := pub_sys_package.get_user; IF v_user_empno = 'SOLIX' THEN --若是是歸檔用戶,則使用原有記錄的數據 v_fcu := infcu; ELSE v_fcu := v_user_empno; END IF; RETURN v_fcu; END; FUNCTION get_fcd(infcd IN VARCHAR2) RETURN DATE IS v_fcd DATE; v_user_empno VARCHAR2(100); BEGIN v_user_empno := pub_sys_package.get_user; IF v_user_empno = 'SOLIX' THEN --若是是歸檔用戶,則使用原有記錄的數據 v_fcd := infcd; ELSE v_fcd := SYSDATE; END IF; RETURN v_fcd; END; FUNCTION get_lcu(inlcu IN VARCHAR2) RETURN VARCHAR2 IS v_lcu VARCHAR2(100); BEGIN v_lcu := pub_sys_package.get_user; IF v_lcu IS NULL THEN v_lcu := inlcu; end IF; RETURN v_lcu; END; FUNCTION get_lcd(inlcd IN VARCHAR2) RETURN DATE IS v_lcd DATE; BEGIN v_lcd := SYSDATE; RETURN v_lcd; END; END pub_sys_package;
插入
create or replace trigger TR_I_table_name before insert on table_name for each row declare --通用變量定義 v_trigger_user varchar2(100); v_trigger_date date; v_sqlcode varchar2(6); v_sqlerrm varchar2(200); v_error_comment varchar2(300); --針對審計字段更新功能定義的遊標和變量 cursor c_switch(cp_switch gbs_tr_switch.switch_for%type) is select status from gbs_tr_switch where trigger_name='TR_I_table_name' and switch_for =cp_switch; v_status gbs_tr_switch.status%type; begin v_error_comment:='before get user'; v_trigger_user :=pub_sys_package.get_user; v_trigger_date :=sysdate; --需求來源:表中記錄的審計字段信息更新 --功能描述:用於保證審計信息的完整性 v_error_comment:='before GBS_insert_4_audit_column'; open c_switch('table_name_in'); fetch c_switch into v_status; if c_switch%found and v_status ='1' then :new.created_by:=v_trigger_user; :new.created_date:=v_trigger_date; :new.updated_by:=v_trigger_user; :new.updated_date:=v_trigger_date; end if; close c_switch; --需球來源:XXXX --功能描述:XXXX --錯誤處理 --觸發器執行有誤,將出錯信息插入到gbs_tr_error_log表 exception when others then v_sqlcode :=sqlcode; v_sqlerrm :=substr(sqlerrm,1,200); insert into gbs_tr_error_log ( error_no , --系統錯誤代碼 error_message , --系統錯誤信息 trigger_name , --出錯的trigger trigger_user , --出錯的用戶 trigger_date , --出錯的時間 error_comment --出錯詳細信息 ) values ( v_sqlcode, v_sqlerrm, 'TR_I_table_name', v_trigger_user, v_trigger_date, v_error_comment ); end;
更新
CREATE OR REPLACE TRIGGER tr_u_table_name BEFORE UPDATE ON table_name FOR EACH ROW DECLARE --通用變量定義 v_trigger_user VARCHAR2(100); v_trigger_date DATE; v_sqlcode VARCHAR2(6); v_sqlerrm VARCHAR2(200); v_error_comment VARCHAR2(300); --針對審計字段更新功能定義的遊標和變量 CURSOR c_switch(cp_switch gbs_tr_switch.switch_for%TYPE) IS SELECT status FROM gbs_tr_switch WHERE trigger_name = 'tr_u_table_name' AND switch_for = cp_switch; v_status gbs_tr_switch.status%TYPE; BEGIN v_error_comment := 'before get user'; v_trigger_user := pub_sys_package.get_user; v_trigger_date := SYSDATE; --需求來源:表中記錄的審計字段信息更新 --功能描述:用於保證審計信息的完整性 v_error_comment := 'before GBS_update_2_audit_column'; OPEN c_switch('table_name_up'); FETCH c_switch INTO v_status; IF c_switch%FOUND AND v_status = '1' THEN :new.updated_by := v_trigger_user; :new.updated_date := v_trigger_date; END IF; CLOSE c_switch; EXCEPTION WHEN OTHERS THEN v_sqlcode := SQLCODE; v_sqlerrm := substr(SQLERRM, 1, 200); INSERT INTO tr_error_log (error_no, --系統錯誤代碼 error_message, --系統錯誤信息 trigger_name, --出錯的trigger trigger_user, --出錯的用戶 trigger_date, --出錯的時間 error_comment --出錯詳細信息 ) VALUES (v_sqlcode, v_sqlerrm, 'tr_u_table_name', v_trigger_user, v_trigger_date, v_error_comment); END;