QT實現支持加密的Sqlite數據庫引擎


  Sqlite數據庫使用很廣泛,我們經常會在發布一些小型軟件的時候使用它,因為它不需要安裝服務器。QT默認的數據庫引擎是支持SQLITE數據庫的,但並不支持對數據庫加密,不加密的Sqlite數據庫任何人都可以很輕易的打開它,這讓我們的數據很不安全,很容易泄露或被篡改。自己對數據庫進行加密當然也可以,但是那就不是通用的了,其他人用其他數據庫工具也無法打開數據庫文件,要想采用通用的加密方式,我們可以在網上找到sqlite3.dll這個動態庫,這個動態庫能夠支持對Sqlite數據庫文件加密。所以如果你想使用加密版的Sqlite,第一種方式就是直接使用sqlite3.dll里面的函數,這種方式簡單,但是你就無法使用QT自帶的數據庫引擎了,這有很多缺點,用過QSqlDatabase的人應該知道,這個類可以打開很多種數據庫類型,比如mysql,sqlserver,sqlite等,而且操作函數都是一致的,這使得我們在更換不同數據庫時很方便,不需要做太大的改動,另外如果你不用QT默認的數據庫引擎,那么就無法使用如QSqlQueryModel進行數據的展示了。

  那么有什么更好的方法嗎?當然有,實際上QT給我們提供了一種方法,就是創建新的數據庫引擎。而且要實現Sqlite非常簡單,幾乎可以用QT自帶的文件做很小的修改就實現。下面我們來講一下具體的操作。

1.創建工程

打開QtCreator,新建項目,選擇Library,C++庫。

類型選擇:Qt plugin,項目名稱SQLITEEX

類名:QSQLiteExDriverPlugin,基類選擇:QSqlDriverPlugin,然后點擊下一步完成創建。

 

2.

找到你安裝QT的目錄,將下面兩個目錄復制到工程目錄下:

D:\Program\Qt\Qt5.10.0\5.10.0\mingw53_32\include\QtCore\5.10.0\QtCore

D:\Program\Qt\Qt5.10.0\5.10.0\mingw53_32\include\QtSql\5.10.0\QtSql

然后在SQLITEEX.pro工程文件添加:

INCLUDEPATH +=$$PWD/QtCore

INCLUDEPATH +=$$PWD/QtSql

 

3.

下載sqlite3.dll,將sqlite3.lib和sliqte3.h文件放到工程目錄下。將sqlite3.h添加到工程。

然后在SQLITEEX.pro工程文件添加:

LIBS += -L$$PWD -lsqlite3

 

4.

新建文件qsql_sqliteex_p.h,qsql_sqliteex.cpp。然后在QT源碼下找到sqlite的實現源碼,例如QT5.10在以下目錄:

D:\Program\Qt\Qt5.10.0\5.10.0\Src\qtbase\src\plugins\sqldrivers\sqlite

找到qsql_sqlite_p.h文件,將#define QSQL_SQLITE_H#endif中間的內容復制到qsql_sqliteex_p.h,

找到qsql_sqlite.cpp文件,將所有內容復制到qsql_sqliteex.cpp。然后將這兩個文件中的類名做下修改:

QSQLiteDriverPrivate修改為:QSQLiteExDriverPrivate

QSQLiteDriver修改為QSQLiteExDriver

QSQLiteResultPrivate修改為QSQLiteExResultPrivate

QSQLiteResult修改為QSQLiteExResult

 

5.修改QSQLiteExDriver::open

找到QSQLiteExDriver::open函數,修改為內容:

bool QSQLiteExDriver::open(const QString & db, const QString & user, const QString & password, const QString & host, int port, const QString & conOpts)
{
    Q_D(QSQLiteExDriver);
    if (isOpen())
        close();


    int timeOut = 5000;
    bool sharedCache = false;
    bool openReadOnlyOption = false;
    bool openUriOption = false;
#ifndef QT_NO_REGULAREXPRESSION
    static const QLatin1String regexpConnectOption = QLatin1String("QSQLITE3_ENABLE_REGEXP");
    bool defineRegexp = false;
    int regexpCacheSize = 25;
#endif

    const auto opts = conOpts.splitRef(QLatin1Char(';'));
    for (auto option : opts) {
        option = option.trimmed();
        if (option.startsWith(QLatin1String("QSQLITE3_BUSY_TIMEOUT"))) {
            option = option.mid(20).trimmed();
            if (option.startsWith(QLatin1Char('='))) {
                bool ok;
                const int nt = option.mid(1).trimmed().toInt(&ok);
                if (ok)
                    timeOut = nt;
            }
        } else if (option == QLatin1String("QSQLITE3_OPEN_READONLY")) {
            openReadOnlyOption = true;
        } else if (option == QLatin1String("QSQLITE3_OPEN_URI")) {
            openUriOption = true;
        } else if (option == QLatin1String("QSQLITE3_ENABLE_SHARED_CACHE")) {
            sharedCache = true;
        }
#ifndef QT_NO_REGULAREXPRESSION
        else if (option.startsWith(regexpConnectOption)) {
            option = option.mid(regexpConnectOption.size()).trimmed();
            if (option.isEmpty()) {
                defineRegexp = true;
            } else if (option.startsWith(QLatin1Char('='))) {
                bool ok = false;
                const int cacheSize = option.mid(1).trimmed().toInt(&ok);
                if (ok) {
                    defineRegexp = true;
                    if (cacheSize > 0)
                        regexpCacheSize = cacheSize;
                }
            }
        }
#endif
    }

    int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
    openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
    if (openUriOption)
        openMode |= SQLITE_OPEN_URI;

    openMode |= SQLITE_OPEN_NOMUTEX;

    if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) {
        if(sqlite3_key(d->access,password.toUtf8(),password.toUtf8().length()) != SQLITE_OK)
        {
            if (d->access) {
                sqlite3_close(d->access);
                d->access = 0;
            }
            setLastError(qMakeError(d->access, tr("Error opening database by key"),
                         QSqlError::ConnectionError));
            setOpenError(true);
            return false;
        }
        sqlite3_busy_timeout(d->access, timeOut);
        setOpen(true);
        setOpenError(false);
#ifndef QT_NO_REGULAREXPRESSION
        if (defineRegexp) {
            auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize);
            sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache, &_q_regexp, NULL,
                                       NULL, &_q_regexp_cleanup);
        }
#endif
        return true;
    } else {
        if (d->access) {
            sqlite3_close(d->access);
            d->access = 0;
        }

        setLastError(qMakeError(d->access, tr("Error opening database"),
                     QSqlError::ConnectionError));
        setOpenError(true);
        return false;
    }
}

 6.QSQLiteExDriverPlugin添加create函數

QSQLiteExDriverPlugin類頭文件添加公有函數:

QSqlDriver* create(const QString &) Q_DECL_OVERRIDE;

 然后實現這個函數:

QSqlDriver *QSQLiteExDriverPlugin::create(const QString &name)
{
    if (name == QLatin1String("QSQLITEEX")) {
        QSQLiteExDriver* driver = new QSQLiteExDriver();
        return driver;
    }
    return 0;
}

 

7.修改SQLITEEX.json文件

修改為:

{
    "Keys" : [ "QSQLITEEX" ]
}

 

8.修改.pro文件

Debug:TARGET = SQLITEEXD
Release:TARGET = SQLITEEX
TEMPLATE = lib
CONFIG += plugin

DESTDIR = ./plugin/sqldrivers

 

9.到此修改完成,編譯工程分別生成debug版的dll和release版的dll,注意發布的時候sqlite3.dll文件要放到exe目錄下。

10.創建測試工程

(1)創建一個控制台程序,

(2)添加代碼

#include <QCoreApplication>
#include <QtSql/QtSql>
#include <QPluginLoader>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
#ifdef QT_NO_DEBUG
  QPluginLoader driverload(qApp->applicationDirPath()+"/plugin/sqldrivers/SQLITEEX.dll");
#else
  QPluginLoader driverload(qApp->applicationDirPath()+"/plugin/sqldrivers/SQLITEEXD.dll");
#endif
    if(driverload.load())
    {
        QSqlDriverPlugin *plugin=qobject_cast<QSqlDriverPlugin*>(driverload.instance());
        if(plugin)
        {
            QSqlDriver *driver=plugin->create("QSQLITEEX");
            QSqlDatabase db;
            db=QSqlDatabase::addDatabase(driver);
            db.setDatabaseName("mydatabase.db");
            db.setPassword("123456");
            if(db.open())
            {
                QSqlQuery qry(db);
                qry.exec("create table t_trade(order_id varchar(100))");
                qry.exec("insert into t_trade(order_id) values('10001')");
                qry.exec("insert into t_trade(order_id) values('10002')");
                qry.exec("select * from t_trade");
                while(qry.next())
                {
                    cout<<qry.value(0).toString().toStdString()<<endl;
                }
            }
        }
        else
            cout<<"get plugin fail"<<endl;
    }
    else
        cout<<"driver load fail"<<endl;
    return a.exec();
}

 (3)將plugin目錄放到生成的exe目錄下:

 

(4)運行結果

 

完整工程下載:https://download.csdn.net/download/jonahking2012/10688526


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM