从 H2 控制台访问加密数据库

Posted

技术标签:

【中文标题】从 H2 控制台访问加密数据库【英文标题】:Access encrypted database from H2 console 【发布时间】:2016-04-08 08:49:22 【问题描述】:

我想将我的 H2 数据库存储为加密文件并访问其 Web 界面。

我配置了H2数据库,这样我就可以访问web界面来操作console/db地址下的数据库了。

当我不使用加密时,一切正常。

当我将CIPHER=AES 添加到db.url 时,我无法登录数据库并出现以下异常:

File corrupted while reading record: null. Possible solution: use the recovery tool [90030-191] 90030/90030 (Help)
org.h2.jdbc.JdbcSQLException: Uszkodzenie pliku podczas wczytywania rekordu: null
File corrupted while reading record: null. Possible solution: use the recovery tool [90030-191]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:168)
    at org.h2.mvstore.db.MVTableEngine$Store.convertIllegalStateException(MVTableEngine.java:195)
    at org.h2.mvstore.db.MVTableEngine$Store.open(MVTableEngine.java:167)
    at org.h2.mvstore.db.MVTableEngine.init(MVTableEngine.java:99)
    at org.h2.engine.Database.getPageStore(Database.java:2460)
    at org.h2.engine.Database.open(Database.java:692)
    at org.h2.engine.Database.openDatabase(Database.java:270)
    at org.h2.engine.Database.<init>(Database.java:264)
    at org.h2.engine.Engine.openSession(Engine.java:65)
    at org.h2.engine.Engine.openSession(Engine.java:175)
    at org.h2.engine.Engine.createSessionAndValidate(Engine.java:153)
    at org.h2.engine.Engine.createSession(Engine.java:136)
    at org.h2.engine.Engine.createSession(Engine.java:28)
    at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:349)
    at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:107)
    at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:91)
    at org.h2.Driver.connect(Driver.java:72)
    at org.h2.server.web.WebServer.getConnection(WebServer.java:735)
    at org.h2.server.web.WebApp.login(WebApp.java:955)
    at org.h2.server.web.WebApp.process(WebApp.java:211)
    at org.h2.server.web.WebApp.processRequest(WebApp.java:170)
    at org.h2.server.web.WebServlet.doGet(WebServlet.java:125)
    at org.h2.server.web.WebServlet.doPost(WebServlet.java:162)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: Store header is corrupt: nio:/home/robak/db/adzfo.mv.db [1.4.191/6]
    at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:773)
    at org.h2.mvstore.MVStore.readStoreHeader(MVStore.java:603)
    at org.h2.mvstore.MVStore.<init>(MVStore.java:353)
    at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2888)
    at org.h2.mvstore.db.MVTableEngine$Store.open(MVTableEngine.java:154)
    ... 47 more

数据库配置

db.driver=org.h2.Driver
db.url=jdbc:h2:file:~/db/app_name;AUTO_SERVER=TRUE;CIPHER=AES
db.username=user
db.password=password

Servlet 上下文

  private void configureDatabaseConsole(ServletContext servletContext) 
    ServletRegistration.Dynamic h2Servlet = servletContext.addServlet("h2console", WebServlet.class);
    h2Servlet.setLoadOnStartup(2);
    h2Servlet.addMapping("/console/db/*");
  

数据来源

  @Bean
  public DataSource dataSource() 
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(getProperty(DRIVER));
    dataSource.setUrl(getProperty(URL));
    dataSource.setUsername(getProperty(USERNAME));
    dataSource.setPassword(FILE_PASSWORD + " " + getProperty(PASSWORD));
    return dataSource;
  

如何将加密数据库作为文件保存在硬盘上并通过 Web 界面访问其控制台?

【问题讨论】:

【参考方案1】:

您确定您使用了正确的密码。引用 H2 文档File Encryption

 The encryption algorithm is set in the database URL, and the file
 password is specified in the password field, before the user 
 password. A single space separates the file password and the user   
 password; the file password itself may not contain spaces. File 
 passwords and user passwords are case sensitive. Here is an example 
 to connect to a password-encrypted database:

 Class.forName("org.h2.Driver");
 String url = "jdbc:h2:~/test;CIPHER=AES";
 String user = "sa";
 String pwds = "filepwd userpwd";
 conn = DriverManager.
     getConnection(url, user, pwds);

如果您不提供正确的密码,您将无法建立连接。

【讨论】:

是的,密码正确dataSource.setPassword(FILE_PASSWORD + " " + getProperty(PASSWORD));。这一定是别的东西。 启用加密时是否创建了新的数据库? 是的,我做到了。我认为可能与AUTO_SERVER=TRUE选项有关,但我仍然没有找到答案。 您是否尝试在连接字符串中没有 AUTO_SERVER 的情况下访问它(将 AES 留在那里)? ps:您还可以使用任何通用 JDBC 客户端(如 SquirrelSQL)访问在服务器模式下运行的 H2。也许你可以检查你是否可以通过这种方式访问​​数据库。 ps:我很快查看了 H2 MVStore 代码,在类的初始化过程中它似乎读取了存储,请参见第 336 行(在您的情况下加密),然后立即尝试读取标题(方法 readStoreHeader() 第 548 行),这也是引发异常的方法(第 593 行)。

以上是关于从 H2 控制台访问加密数据库的主要内容,如果未能解决你的问题,请参考以下文章

H2 控制台无法访问 jbdc 数据库

使用 webAllowOthers 远程访问 H2 控制台

H2 数据库服务器从连接池为 TCP 客户端提供服务

从控制台创建 H2 数据库

使用 h2-browser 访问 play 项目数据库时用户名错误

docker 容器中的 H2 DB