随着移动互联网的不断发展,移动应用的需求越来越趋向于高效、快速、安全。而在这种背景之下,数据库的安全性也愈加受到重视。作为移动应用常用的数据库之一,Greendao数据库在实际应用中越来越受到开发人员的关注。然而,由于其中存储的数据非常重要,如果没有进行必要的保护,那么就容易遭受数据库信息被破解、篡改的情况。因此,如何保护Greendao数据库的数据安全性,成为众多开发者需要解决的问题。本文将介绍怎样使用加密算法的方法对Greendao数据库进行保护。
一、什么是Greendao数据库
Greendao数据库是一个快速的ORM(面向对象的关系映射)框架,旨在为Android开发者提供高效、可靠的数据库操作支持。 它提供了一个易于使用的对象映射器,使开发人员可以使用Java对象来存储和查询数据。Greendao库使用传统的SQLite数据库存储数据,并提供了与之配套的操作API,使得开发人员可以在Android应用程序中简单地存储、修改和查询数据。使用Greendao框架存储数据可以极大地增强程序的性能,但为了保证数据的安全性,需要对其中存储的信息进行加密保护。
二、为什么需要加密
在移动应用开发中,数据安全性一直是一个非常重要的话题。在用户日常使用的过程中,可能会存储一些敏感的信息,例如银行账号、信用卡信息、个人隐私等等,如果没有进行加密保护,就容易遭受黑客的攻击。加密则是保护用户数据独立性的基本手段。在数据传输、数据存储等方面加密技术得到广泛的应用。当然,对于移动应用来说,移动设备的内存、处理器等硬件资源有限,因此在加密中要选择适合移动设备的算法。加密技术可以通过使用算法使数据难以被读取,而只有使用解密方法才能获得原始数据。在保证数据不被盗取、篡改等意外情况的同时,加密方法还能防止未授权的人员对数据库的访问,保障了数据库的机密性和完整性。
三、greendao数据库加密方法
要实现greendao数据库加密的功能,我们需要使用第三方加密库——SQLCipher。SQLCipher 是基于 SQLite 的一个开源项目,它采用了 AES 算法来对 SQLite 数据库进行加密,为 SQLite 提供了完美的安全性,同时兼容 SQLite原有的 API。下面介绍具体步骤:
1. 导入SQLCipher库
为了使用SQLCipher库,首先需要将其导入到项目当中。将以下行添加到你的 build.gradle 文件中,同步项目即可:
“`
implementation ‘net.zetetic:android-database-sqlcipher:4.0.1’
“`
2. 生成加密密钥
在使用SQLCipher的时候,需要提供一个加密密钥,用于加密和解密数据库。同时,为了保证密钥的安全性,建议将key存储在Android KeyStore中。
“`
private static void generateSecretKey() {
try {
KeyStore keyStore = KeyStore.getInstance(“AndroidKeyStore”);
keyStore.load(null);
if (!keyStore.contnsAlias(KEY_ALIAS)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, “AndroidKeyStore”);
keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
} catch (Exception e) {
e.printStackTrace();
}
}
“`
3. 创建加密DatabaseHelper
twicejie在Github上提供了一种实现,即重写SQLiteOpenHelper的方法,使用个性化的DatabaseHelper类进行加密。具体可见官方文档。
“`
public class SecureDatabaseOpenHelper extends SQLiteOpenHelper {
private static final int SCHEMA_VERSION = 1;
private static final byte[] PASSWORD = “thirtytwocharactersorless”.getBytes();
public SecureDatabaseOpenHelper(Context context, String name) {
super(context, name, null, SCHEMA_VERSION,
new SQLiteDatabaseHook() {
@Override
public void preKey(SQLiteDatabase database) {}
@Override
public void postKey(SQLiteDatabase database) {
try {
database.execSQL(“PRAGMA cipher_migrate;”);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(“CREATE TABLE foo (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);”);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
“`
4. 创建加密DatabaseManager
然后你的代码就是从这个管理类中读写了。需要提供一个解密的回调、加密回调、关闭数据库回调,这是SQLCipher需要的。DatabaseManager即对Database进行CURD操作的管理类,其中打开数据库的时候调用了自定义的SecureDatabaseOpenHelper类来创建或打开数据库。
“`
public class SecureDatabaseManager {
private static final String DATABASE_NAME = “secure-db”;
private static final String TABLE_NAME = “table_name”;
private static SecureDatabaseManager sInstance;
private static SQLiteDatabase database;
private SecureDatabaseManager() {
database = new SecureDatabaseOpenHelper(App.getContext(), DATABASE_NAME).getWritableDatabase(PASSWORD);
}
public static synchronized SecureDatabaseManager getInstance() {
if (sInstance == null) {
sInstance = new SecureDatabaseManager();
}
return sInstance;
}
public static void closeDatabase() {
try {
if (database != null && database.isOpen()) {
database.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setWriteAheadLoggingEnabled(boolean enabled) {
database.enableWriteAheadLogging();
}
interface ExecuteQuery {
void executeQuery(SQLiteDatabase database) throws Throwable;
}
public interface EncryptionCallback {
void encryptionCallback(SQLiteDatabase database);
}
public interface DecryptionCallback {
void decryptionCallback(SQLiteDatabase database);
}
public void executeQuery(ExecuteQuery executeQuery) {
try {
executeQuery.executeQuery(database);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public void executeQuery(String sql) {
try {
database.execSQL(sql);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
// SQLCipher requires an explicit transaction to encrypt/decrypt
public void encryptDatabase(EncryptionCallback encryptionCallback) {
database.beginTransaction();
try {
database.rawExecSQL(String.format(“ATTACH DATABASE ‘%s’ AS encrypted KEY ‘%s’;”, DATABASE_NAME + “-encrypted”, new String(PASSWORD)));
database.rawExecSQL(“SELECT sqlcipher_export(‘encrypted’)”);
database.rawExecSQL(“DETACH DATABASE encrypted;”);
encryptionCallback.encryptionCallback(database); // Do all other database writes
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
// SQLCipher requires an explicit transaction to encrypt/decrypt
public void decryptDatabase(DecryptionCallback decryptionCallback) {
database.beginTransaction();
try {
database.rawExecSQL(String.format(“ATTACH DATABASE ‘%s’ AS plntext KEY ”;”, DATABASE_NAME + “-plntext”));
database.rawExecSQL(“SELECT sqlcipher_export(‘plntext’)”);
database.rawExecSQL(“DETACH DATABASE plntext;”);
decryptionCallback.decryptionCallback(database); // All writes to the database can happen here
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
}
“`
5. 其他操作
通过以上步骤,即可实现对Greendao数据库的加密保护。对于其他对数据库的操作,都可在SecureDatabaseManager中定义出相应的方法进行实现。例如:
“`
public List getItems() {
List itemList = new ArrayList();
SecureDatabaseManager.getInstance().executeQuery(new SecureDatabaseManager.ExecuteQuery() {
@Override
public void executeQuery(SQLiteDatabase database) throws Throwable {
Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null);
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex(“id”));
String name = cursor.getString(cursor.getColumnIndex(“name”));
Item item = new Item();
item.setId(id);
item.setName(name);
itemList.add(item);
}
cursor.close();
}
});
return itemList;
}
public void addItem(final Item item) {
SecureDatabaseManager.getInstance().executeQuery(new SecureDatabaseManager.ExecuteQuery() {
@Override
public void executeQuery(SQLiteDatabase database) throws Throwable {
ContentValues contentValues = new ContentValues();
contentValues.put(“id”, item.getId());
contentValues.put(“name”, item.getName());
database.insert(TABLE_NAME, null, contentValues);
}
});
}
“`
相关问题拓展阅读:
- Android Greendao插入10万条数据OOM
- greendao怎样定义复合主键
Android Greendao插入10万条数据OOM
下面写个小程哪念仿序测试一下。
private Runnable runnable = new Runnable() { @Override
public void run() {
List bookList = new ArrayList();for (int i = 0; i
Book book = new Book();
book.setUuid(UUID.randomUUID().toString());
book.setName(“name”);//其他set方法略
bookList.add(book);
}try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBookDao.insertOrReplaceInTx(bookList);
Log.d(TAG, “插入book数据:” + bookList.size());
}
};private void insert() {
Log.d(TAG, “
线程池
开始”);
mBookDao.deleteAll(); long time = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i
executorService.submit(runnable);
}
executorService.shutdown(); for (; ; ) {if (executorService.isTerminated()) {break;
}try {
executorService.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, “线程池完成:” + (System.currentTimeMillis() – time) + “ms”);
}
runnable任务模拟1秒从网络拉取5000条数据并插入DB,insert方法使用线程池执李纤行runnable任务。
执行时间超过1000秒,查看内存占用超过180M。如果数据量更多,肯定会发生OOM,基本上可以定位是greenDAO的问题。现在需要在两个方面优化,一是寻找内存占用的原因,二是提高数据的插入速度。
查看内存堆
内存的占用随着insert的数据量越多而递增,从中间dump出java堆,得到hprof文件。注意这个文件不是标准格式,只能用AndroidStudio打开。
图1
右击文件导出标准的hprof文件,用更加强大的MAT分析。
图2
图3
看到IdentityScope占了一半内存,可以确定是greenDAO缓存了插入数据。
mBookDao.insertOrReplaceInTx(bookList);mBookDao.detachAll();
greenDAO的缓存功能是有用的,没必要关闭,改成在插入数据后,调用一次detachAll,将identityScope清空。
public void detachAll() { if (identityScope != null) {
identityScope.clear();
}
}
重建索引
对表插入大量数据,如果中间高唤没有涉及到业务,可以先失效索引,待插入完成后重建索引。
String sql = “drop index index_in”;
mDb.execSQL(sql);
sql = “drop index index_publisherid”;
mDb.execSQL(sql);
sql = “drop index index_author”;
mDb.execSQL(sql);
插入数据前,drop掉表中的索引。没有见到greenDAO有操作索引的方法,直接执行sql命令。
sql = “create index index_in on book(in)”;
mDb.execSQL(sql);
sql = “create index index_publisherid on book(publisherid)”;
mDb.execSQL(sql);
sql = “create index index_author on book(author)”;
mDb.execSQL(sql);
插入数据完成后,重建索引。最后执行100w数据插入大约耗时450秒,比什么都不做快了两三倍。
异步操作
上一个步骤的耗时包含了模拟网络和数据库操作的时间,使用
多线程
将两个环节分离,可以减少总时间。
greenDAO提供了AsyncSession这个异步操作类,使用daoSession.startAsyncSession()获取实例,内部实现使用了线程池和阻塞队列,原理很简单不用多讲。
mAsyncSession.runInTx(new Runnable() { @Override
public void run() {
mBookDao.insertOrReplaceInTx(bookList);
mBookDao.deleteAll();
}
});
获取数据后,提交给AsyncSession异步插入数据库。要注意在合适地方使用waitForCompletion,等待AsyncSession完成已有任务。如果获取数据速度很快,而操作数据库很慢,会导致过多数据缓存在AsyncSession的内部阻塞队列。
最后测试一下100w数据插入数据库,耗时不到150秒,又快了几倍。
作者:展翅而飞
链接:
来源:
简书
著作权
归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
greendao怎样定义复合主键
我使用 Guid 作为我一个服务器应用程序中的主键樱兄,并且想要能够远程从 android 设备和上载回服务器生成新的数据。Android 设备上的数据库是在 sqlite 中,并使用纯颂肢 greenDAO 来生成 Pojo 和数据访问层。我正在使用 Guid 时,为避免主关键碰撞的数据上载到服务器。我作为字符串存储 Guid。
说应创建第二个域持有该字符串仍使用长主键青睐的 greendao,但这意味着我必须重新连接我的所有数据库关系时导入数据从服务器到 app,是一种痛苦的 greendao 网站上有一些更多的意见。将多宁愿只是继做世续使用字符串主键,如果这是可能的。
关于greendao数据库加密的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。