2012-09-27 32 views
2

我正在爲沒有網絡連接的環境編寫桌面Java應用程序。我試圖儘可能安全地將應用程序數據存儲在加密的進程內hsqldb中,並使用未加密的用戶信息hsqldb。 Hsqldb要求在創建連接時將crypto_key設置在jdbcurl中。我的應用程序使用hibernate做持久性,Spring使用配置和注入。在運行時延遲更改SessionFactory數據源jdbcurl

我目前的計劃是在未加密的用戶表中存儲用戶名,密碼哈希,salt和加密數據庫的crypto_key。 crypto_key通過使用用戶密碼作爲密鑰的非對稱加密來保護。因此,應用程序不知道應用程序數據的crypto_key是什麼,直到它運行了足夠長的時間才能加載gui並對用戶進行身份驗證。

這是我當前的applicationContext.xml。 Spring使用它來讓Hibernate正常運轉。

<?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.1.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> 

<context:component-scan base-package="com.company.domain" /> 
<context:component-scan base-package="com.company.service" /> 

<tx:annotation-driven /> 

<bean id="userDataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 
    <property name="url" 
     value="jdbc:hsqldb:./ReviewDatabase/users" /> 
    <property name="username" value="reviewer" /> 
    <property name="password" value="$kelatonKey" /> 
</bean> 


<bean id="mainDataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> 
    <property name="url" 
     value="jdbc:hsqldb:./ReviewDatabase/data" /> <!-- TODO: ;crypt_key=;crypt_type=AES --> 
    <property name="username" value="reviewer" /> 
    <property name="password" value="$kelatonKey" /> 
</bean> 

<bean id="userSessionFactory" 
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="userDataSource" /> 
    <property name="annotatedClasses"> 
     <list> 
      <value>com.company.domain.AppUser</value> 
     </list> 
    </property> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> 
      <prop key="hibernate.show_sql">true</prop> 
      <prop key="hibernate.hbm2ddl.auto">update</prop> 
     </props> 
    </property> 
</bean> 

<bean id="mainSessionFactory" 
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="mainDataSource" /> 
    <property name="annotatedClasses"> 
     <list> 
<!--    <value>com.companu.domain.Person</value> --> 
<!--    <value>com.company.domain.Thing</value> --> 
<!--    <value>com.company.domain.Thing1</value> --> 
<!--    <value>com.company.domain.Thing2</value> --> 
<!--    <value>com.company.domain.Review</value> --> 
     </list> 
    </property> 
    <property name="hibernateProperties"> 
     <props> 
      <pro key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> 
      <prop key="hibernate.show_sql">true</prop> 
      <prop key="hibernate.hbm2ddl.auto">update</prop> 
     </props> 
    </property> 
</bean> 


<bean id="mainTransactionManager" 
    class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="mainSessionFactory" /> 
</bean> 

<bean id="userTransactionManager" 
    class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="userSessionFactory" /> 
</bean> 
</beans> 

這裏的注入

@Repository("ReviewDao") 
public class HibernateReviewDao implements ReviewDao{ 

private SessionFactory mainSessionFactory; 

@Autowired 
public void setMainSessionFactory(
     SessionFactory mainSessionFactory){ 
    this.mainSessionFactory = mainSessionFactory; 
} 

@Override 
@Transactional(value = "mainTransactionManager") 
public void store(Review review) { 
    mainSessionFactory.getCurrentSession().saveOrUpdate(review); 

} 

@Override 
@Transactional(value = "mainTransactionManager") 
public void delete(Long reviewId) { 
    Review review = (Review)mainSessionFactory.getCurrentSession() 
      .get(Review.class, reviewId); 
    mainSessionFactory.getCurrentSession().delete(review); 
} 
} 

最後,這裏就是我試圖對用戶進行認證,並獲得該crypto_key後做的一類,我想有一個SessionFactory的實例。

String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data2;crypt_key=" + secret + ";crypt_type=AES"; 
    ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() 
    .applySetting("hibernate.dialect", "org.hibernate.dialect.HSQLDialect") 
    .applySetting("hibernate.show_sql", "true") 
    .applySetting("hibernate.hbm2ddl.auto","update") 
    .applySetting("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver") 
    .applySetting("hibernate.connection.url", jdbcUrl) 
    .applySetting("hibernate.connection.username", "reviewer") 
    .applySetting("hibernate.connection.password", "$kelatonKey") 
    .buildServiceRegistry(); 

    SessionFactory mainSessionFactory = new MetadataSources(serviceRegistry) 
     .addAnnotatedClass(com.company.domain.Review.class) 
     .addAnnotatedClass(com.company.domain.Person.class) 
     .addAnnotatedClass(com.company.domain.Thing.class) 
     .addAnnotatedClass(com.company.domain.Thing1.class) 
     .addAnnotatedClass(com.company.domain.Thing2.class) 
     .buildMetadata() 
     .buildSessionFactory(); 
    org.springframework.orm.hibernate4.HibernateTransactionManager htm = 
      (HibernateTransactionManager)context.getBean("mainTransactionManager"); 
    context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory"); 
    htm.setSessionFactory(mainSessionFactory); 

然而,與第一個查詢到上述結果的對象org.hibernate.HibernateException: No Session found for current thread

我怎樣才能改變休眠已初始化後不久JDBCURL,依賴已經注入等各種TOM-foolery的已經發生了? 我一直在關注這部分的發展,希望谷歌最終能夠通過,但我沒有想法尋找。所有的答案都將被羞怯地接受:)

回答

0

我想知道這是否會有所幫助,Can I replace a Spring bean definition at runtime?,您可以虛擬出bean屬性,然後在運行時更改bean。

+0

24小時後,我想出了一些有用的東西,但這個鏈接背後的答案可以更通用地應用(以一種好的方式)。加入一個關於如何使用LocalSessionFactoryBean的想法,我會稱這個解決。 –

+0

這很酷 – drobson

0

所以,配方丟失的位是LocalSessionFactoryBean。它獲得了sessionFactory設置,所以我可以替換在初始化時創建的sessionFactory。 下面的代碼,我不得不從這個問題

org.springframework.orm.hibernate4.HibernateTransactionManager htm = 
      (HibernateTransactionManager)context.getBean("mainTransactionManager"); 
    Class<?>[] classes = new Class<?>[5]; 
    classes[0] = com.company.domain.Thing1.class; 
    classes[1] = com.company.domain.Thing2.class; 
    classes[2] = com.company.domain.Person.class; 
    classes[3] = com.company.domain.Thing.class; 
    classes[4] = com.company.domain.Review.class; 

    String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data3;crypt_key=" + secret + ";crypt_type=AES"; 

    java.util.Properties hibernateProperties = new java.util.Properties(); 
    hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); 
    hibernateProperties.setProperty("hibernate.show_sql", "true"); 
    hibernateProperties.setProperty("hibernate.hbm2ddl.auto","update"); 
    hibernateProperties.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver"); 
    hibernateProperties.setProperty("hibernate.connection.url", jdbcUrl); 
    hibernateProperties.setProperty("hibernate.connection.username", "reviewer"); 
    hibernateProperties.setProperty("hibernate.connection.password", "$kelatonKey"); 


    LocalSessionFactoryBean slfb = new LocalSessionFactoryBean(); 
    slfb.setHibernateProperties(hibernateProperties); 
    slfb.setAnnotatedClasses(classes); 
    try { 
     slfb.afterPropertiesSet(); 
    } catch (IOException e) { 
     Log.warn("Cannot connection to application database"); 
     Log.write(e.getLocalizedMessage()); 
     Log.write(e.getStackTrace()); 
     return; 
    } 
    SessionFactory mainSessionFactory = slfb.getObject(); 
    context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory"); 

    htm.setSessionFactory(mainSessionFactory); 
    for(ListenForNewSessionFactory dao : daos){ 
     dao.setNewSessionFactory(mainSessionFactory); 
    } 

我不得不每道實現一個接口設置SessionFactory的改變,並有他們每個人將自己添加到一個靜態列表上的初始化。它不是很可重用,但它可以工作。

0

我使用了下面的黑客 - 無論我需要SessionFactory,我用SessionFactoryFactory(下面)代替 - 我委託實際使用的唯一SessionFactory方法。

@Component 
public class SessionFactoryFactory { 
    @Autowired 
    private LocalSessionFactoryBean sessionFactoryBean; 

    @Autowired 
    private DriverManagerDataSource dataSource; 

    private SessionFactory sessionFactory; 

    private SessionFactory getSessionFactory() { 
     if (null == sessionFactory) { 
      sessionFactory = sessionFactoryBean.getObject(); 
     } 
     return sessionFactory; 
    } 

    public Session openSession() { 
     return getSessionFactory().openSession(); 
    } 

    public void updateDataSourceUrl() throws IOException { 
     sessionFactory = null; 
     sessionFactoryBean.afterPropertiesSet(); 
    } 
}