开源库介绍

devbean/QtCipherSqlitePlugin: A Qt plugin for cipher SQLite. (github.com)

该加密库是基于 SQLite 和 wxSQLite3 源代码开发的的一个加密 SQLite 的Qt插件。总结就是,可以使用该插件对 SQLite 的数据库进行加密。

开始

  1. 在开始之前,可以去仓库的 wiki 界面找到相应的开始教程,以下操作参考 wiki 。
  1. 拉取该库到本地后,打开 QtCipherSqlitePlugin.pro,就能看到如下图所示的工程结构。

    image-20220508222820701

    1. demo 目录是一个如何使用该加密插件的工程,涉及数据库的加密、打开、创建等
    2. sqlitecipher是我们编译 dll 的工程文件
    3. Qt 的插件测试工程,里边有关于该加密库的详细使用方式介绍。
  2. 不论是使用上述测试工程中的哪个,我们第一步都需要编译插件的动态库出来使用。

    1. 单独sqlitecipher.pro
    2. sqlitecipher.pro 调整 Qt kits ,笔者的环境为 MSVC 2015 32 bit, Release模式
    3. 右击项目文件,点击部署即可
    4. 到工程所在的文件夹,就能看到 build-sqlitecipher-Desktop_Qt_5_9_9_MSVC2015_32bit-Release 的文件夹,在 .\plugins\sqldrivers下就能看到生成的动态库文件,动态库 sqlitecipher.dll
    5. 拷贝该文件到你的 Qt 目录下 kits 对应的文件夹当中。如笔者的为 $Qt安装目录$\5.9.9\msvc2015\plugins\sqldrivers

使用

到这一步,我们就需要查看一下动态库是否可用了。使用 Qt#include <QSqlDatabase>中的方法即可:

qDebug() << QSqlDatabase::drivers();

执行上述代码,如果加载成功的话,我们会在 qDebug() 的输出中看到相应的数据库名。

("QSQLITE", "QMySQL", "QMySQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7", "SQLITECIPHER")

以上代码,在 demo 所在工程中都能找到对应的代码,不用读者编写。

另外,使用 SQLITECIPHER 数据库也可以打开普通未加密的 QSQLITE 创建的数据库。

加密

加密数据库的使用也简单,区别于普通数据库的方式是需要设置连接属性。以该开源库为例,其主要通过三个连接属性去决定是否设置密码。主要涉及到的关键字为以下三个:

  • Added by QtCipherSqlitePlugin
    • QSQLITE_CREATE_KEY
    • QSQLITE_UPDATE_KEY
    • QSQLITE_REMOVE_KEY

还有其他几个继承自 SqlitePlugin 的关键字,用法与 SqlitePlugin 打开数据库设置一致。

  • Provided by Qt SqlitePlugin
    • QSQLITE_BUSY_TIMEOUT
    • QSQLITE_OPEN_READONLY
    • QSQLITE_OPEN_URI
    • QSQLITE_ENABLE_SHARED_CACHE

以下代码演示三种关键字的用法。

创建数据库密码 QSQLITE_CREATE_KEY

如果输出中有 SQLITECIPHER 的名字,那么恭喜你,插件没有问题! 为没有加密的数据库增加密码 Qt 默认提供的 SQLite 插件是没有加密功能的。新版本的 QtCipherSqlitePlugin 支持为原本没有加密的数据库增加密码,使用方法如下: C++

QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
dbconn.setDatabaseName("test.db");
dbconn.setPassword("test");
dbconn.setConnectOptions("QSQLITE_CREATE_KEY"); 
if (!dbconn.open()) {    
    qDebug() << "Can not open connection: " << dbconn.lastError().driverText();    
    exit(CONNECTION_FAILED);
}

上面的代码,我们使用 test.db 数据库,将密码设置为 test,同时指定连接选项为 QSQLITE_CREATE_KEY 。此时,调用 open() 函数之后,QtCipherSqlitePlugin 将使用改密码为这个数据库进行加密。

删除数据库密码 QSQLITE_REMOVE_KEY

QtCipherSqlitePlugin 可以删除数据库密码,此时需要提供原密码,并使用连接选项 QSQLITE_REMOVE_KEY ,如下:

QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
dbconn.setDatabaseName("test.db");
dbconn.setPassword("test");
dbconn.setConnectOptions("QSQLITE_REMOVE_KEY");
if (!dbconn.open()) {   
    qDebug() << "Can not open connection: " << dbconn.lastError().driverText();   
    exit(CONNECTION_FAILED);
}

更新数据库密码 QSQLITE_UPDATE_KEY

QtCipherSqlitePlugin 可以更新数据库原有密码,需要设置原密码,并且使用连接选项 QSQLITE_UPDATE_KEY 设置新密码,具体代码如下:

QSqlDatabase dbconn = QSqlDatabase::addDatabase("SQLITECIPHER");
dbconn.setDatabaseName("test.db");
dbconn.setPassword("test");
dbconn.setConnectOptions("QSQLITE_UPDATE_KEY=newtest"); 
if (!dbconn.open()) {    
    qDebug() << "Can not open connection: " << dbconn.lastError().driverText();   
    exit(CONNECTION_FAILED);
}

如果原密码不正确,QtCipherSqlitePlugin 会直接返回错误。

如果新密码设置为空,例如 QSQLITE_UPDATE_KEY= ,则作用等同于删除密码。同样的,设置 setConnectOptions("") 也相当于清除设置的属性。

补充

以下代码是我个人想写一个通用的设置属性的接口,后来思考一番不太现实以示众多读者,不要重蹈覆辙。

bool setDBConfig(QSqlDatabase* dbconn, QMap<enum_SQLKEY, QString> qMapCfg)
{
	for (auto it : qMapCfg.toStdMap())
	{
		if (it.first <= QSQLITE_REMOVE_KEY)
		{
			dbconn->setPassword(it.second);
			dbconn->setConnectOptions(GETOBJNAME(it.first));
		}
		else if (it.first == QSQLITE_UPDATE_KEY)
		{
			dbconn->setPassword(it.second);
			dbconn->setConnectOptions(QString("%1=%2").arg().arg());
		}
		
	}
}