本文转自郭霖大神的微信文章,排版稍作修改,原文链接:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650240766&idx=1&sn=096f029aa531d5d99191c3d4c9126fc1&chksm=88638791bf140e87721abb26b32b68233153ccff50f2940d173cd5108bff42287d42db9304d9&mpshare=1&scene=23&srcid=0815v9TSoabJE5PjZ4fmcXyR#rd
# 1,加密功能概述 #
一直以来,我们使用LitePal将数据存储到数据库中都是直接以明文形式存储的。虽说各个应用的数据库都是存放在独立的沙盒环境中,无法被其他应用所访问,也无法被用户看到,但是如果用户将手机ROOT了之后,就可以随意地查看每个应用的数据库文件,所有数据一览无余。当然,会去ROOT手机的用户毕竟在少数,因此大多数情况下,我们可能并不需要考虑这种情况。但是,如果你存储在数据库中的数据真的十分机密,并且要求较高安全性的话,那么最好还是加密一下再存储到数据库当中。
不过,之前的LitePal版本并不支持数据加密功能,因此如果想要实现这个功能还得靠大家自己去写加解密算法。值得高兴的是,从1.6.0版本开始就不用再这么麻烦了,LitePal内置了对数据进行加解密的功能,并且,用法还是一如既往的简单。
1.6.0版本中主要支持了两种加密算法,AES和MD5,那么接下来我们就一个个看。
## 1.1 AES加密 ##
AES加密算法想必大家应该都不会陌生,它的全称是Advanced Encryption Standard,中文名叫高级加密标准,同时它也是美国联邦政府采用的一种区块加密标准。我们如果使用它来对数据进行加密的话,则可以大大提升数据的安全性。
那么接下来我们就来看一下如何使用LitePal来对存储的数据进行加密吧。比如我们有一个Book类,类中有一个name字段和一个page字段,现在我们希望将name字段的值进行加密,那么只需要这样写:
public class Book extends DataSupport {
@Encrypt(algorithm = AES)
private String name;
private int page;
// getter and setter
}
没错,就是这么简单。只需要在name字段的上方加上@Encrypt(algorithm = AES)这样一行注解即可,其他的任何操作都无需改变。我们原来该怎样存储数据还是怎样存储数据,比如使用如下的代码向book表中插入一条数据:
Book book = new Book();
book.setName("第一行代码");
book.setPage(500);
book.save();
代码很简单,我们向book表中插入了一条记录,书名是第一行代码,书的页数是500。那么现在我们到数据库中来查看一下这条数据,结果如下图1所示:
可以看到,这里书名已经被加密了,我们直接查看数据库将完全无法得知它真实的数据是什么。
更加方便的是,这种AES加密只是针对于破解者的一种防护措施,但是对于开发者而言,加解密操作是完全透明化的。也就是说,作为开发者我们并不用考虑某个字段有没有被加密,然后要不要进行解密等等,我们只需要仍然使用标准的LitePal API来查询数据即可。
比如使用如下的代码从book表中查询这条数据,并将结果打印出来:
Book book = DataSupport.findFirst(Book.class);
String name = book.getName();
int page = book.getPage();
Log.d(TAG, "book name is " + name);
Log.d(TAG, "book page is " + page);
打印结果如下图2所示:
可以看到,这里我们查询出来的结果直接就是明文,LitePal在后台已经默默帮我们做了解密操作了,因此整个加解密工作对于开发者而言都是完全透明的。
没错,LitePal的AES加解密功能就是这么简单,现在你已经学会了。
不过除了上面这些基本功能之外,还有一些细节可能也是你需要知道的。
第一点细节,你可以为AES算法来指定一个你自己的加密密钥。使用不同的密钥,加密出来的结果也是不一样的。如果你没有指定密钥,LitePal会使用一个默认的密钥来进行加密。因此,尽可以地调用LitePal.aesKey()方法来指定一个你自己的加密密钥,这样会让你的数据更加安全。
第二点细节,AES算法包括还有下面即将要介绍的MD5算法都只对String类型的字段有效,如果你尝试给其他类型的字段(比如说int字段)指定@Encrypt注解,LitePal并不会执行任何加密操作。
第三点细节,加密后的数据字段不能再通过where语句来进行查询、修改或删除。也就是说,执行类似于 where("name = ?", "第一行代码") 这样的语句将无法查到任何数据,因为在数据库中存储的真实值已经不是第一行代码了。
这样你就将AES加密的功能都学完了,那么接下来我们开始学习MD5加密的功能。
## 1.2 MD5加密 ##
MD5加密算法大家肯定更不会陌生了吧,这个算法实在是太常见了。它的全称是Message Digest Algorithm 5,中文名叫信息摘要算法第五版。要说到MD5加密算法的特点其实有很多很多,但是它最为突出的一个特点就是,使用这种加密算法计算出来的结果是不可逆的。通俗点来说,就是MD5算法只能进行加密但不能进行解密。
那有的朋友可能会疑惑了,如果数据加密了之后就不能再解密,那我要这个数据还有什么用?然而,实际上确实存在着不少场景是不用对数据进行解密的。
比如说用户的密码,密码就是属于安全性要求非常高的数据,直接将密码的明文存储在数据库中是一件非常危险的事情,因此这种情况下我们一定要对数据进行加密才行。但是如果使用上述的AES算法来对密码进行加密可能并不是一个好主意,因为AES加密的数据是可以被解密的,一旦我们的密钥泄漏了出去,所有用户的密码就都有可能被解密出来。
因此,这种情况下使用类似于MD5这种不可逆的加密算法才是最好的选择。因为密码这类数据完全不需要解密,验证用户输入的密码是否正确只需要将输入的内容同样使用MD5算法加密一下,然后和数据库中存储的值进行对比就可以了。
介绍了MD5加密的使用场景,接下来我们就学习一下如何在LitePal中使用MD5算法来加密数据吧。
其实用法想必你也已经能猜到了,真的是非常非常简单,如下所示:
public class User extends DataSupport {
@Encrypt(algorithm = MD5)
private String password;
private String username;
// getter and setter
}
没错,其实和前面的AES加密基本是一模一样的用法,我们只需要将@Encrypt中指定的加密算法改成MD5即可。
现在我们使用同样的代码来存储一条数据:
User user = new User();
user.setUsername("guolin");
user.setPassword("123456");
user.save();
然后到数据库中查看一下,结果如下图3所示:
可以看到,数据同样被加密了,但是密文的格式明显和刚才的AES加密不一样了。
那么接下来我们还是使用刚才的代码来查询数据并打印出来:
User user = DataSupport.findFirst(User.class);
String username = user.getUsername();
String password = user.getPassword();
Log.d(TAG, "username is " + username);
Log.d(TAG, "password is " + password);
打印结果如下图4所示:
# 2,将数据库保存到SD卡 #
这个功能其实一直以来呼声都很高,但是我却迟迟都没有去实现,因为我觉得将数据库文件保存到SD卡是一件很不安全的事情,这意味着应用程序的数据将会面临泄漏的风险。
但是由于1.6.0版本开始支持了数据加密功能,因此我觉得是时候应该开放将数据库文件保存到SD卡这个功能了。大家现在可以自行判断哪些数据需要加密,哪些数据不需要,确保自己的应用数据的安全性就可以了。
当然,肯定也有一些朋友会感到疑惑,我为什么要将数据库文件保存到SD卡呢?有什么好处没有?其实在绝大多数情况下,我都是非常不建议将数据库文件保存到SD卡的,但是保存到SD卡也确实存在着以下一些好处:
第一,方便调试。因为数据库的默认存储路径是在应用的沙盒当中,即使作为开发者的我们也是访问不到的。因此有时候编程出现了问题,但是我们又看不到数据库原文件,调试起来非常不方便。而将数据库文件保存到SD卡,我们就可以轻易地访问到了。
第二,应用卸载后数据不丢失。由于数据库文件默认存储在应用的沙盒目录当中,一旦应用被卸载了,数据库文件也会一同被清除。如果你想实现这样的功能:应用被卸载了,然后用户又重新安装,依然能够读取到之前的数据,那么就必须将数据库文件保存到SD卡。
介绍完了使用场景,接下来我们就看一下如何将数据库文件保存到SD卡吧。
我在设计LitePal的接口时,永远都会将易用性放在第一位,因此你已经可以猜到,这个功能同样非常简单。假如我们希望将数据库文件保存到SD卡的 guolin/database目录下,只需要修改litepal.xml中的配置即可,如下所示:
<litepal>
...
<storage value="guolin/database" />
</litepal>
没错,就是这么简单。注意不需要将SD卡的完整路径配置进去,只需要配置相对路径即可。
另外还有非常重要的一点需要注意,由于从Android 6.0开始访问SD卡需要申请运行时权限,而LitePal是不会去帮你申请运行时权限的(因为LitePal中既没有Activity也没有Fragment),因此如果你选择将数据库文件存储在SD卡上,那么请一定要确保你的应用程序已经对访问SD卡权限进行了运行时权限处理,否则LitePal的所有操作都将会失败。
好了,LitePal 1.6.0版本中最主要的一些功能就介绍的差不多了。当然除了这些功能之外,我还修复了一些已知的bug。如果你觉得这些新功能正是你所需要的话,那就赶快升级吧。
# 3,如何升级 #
升级方式一如既往的简单,如果你使用的是Android Studio,只需要在build.gradle中修改一下配置即可:
dependencies {
compile 'org.litepal.android:core:1.6.0'
}
1.6.0版本中的所有的功能都是向下兼容的,因此你的升级不用付出任何成本。
如果你使用的还是Eclipse,那么就需要到LitePal的项目主页去下载最新版的jar包了,项目主页地址是:
https://github.com/LitePalFramework/LitePal