android 指紋驗證

參考文章

首先是安卓提供的接口姨蝴,所有關(guān)于指紋識別的接口全部在handroid.hardware.fingerprint的這個類中籽懦,這個包中包含的內(nèi)容不是很多,如下圖:

image

api官方鏈接

上面的圖中八拱,我們看到這個包中總共有4個類藏雏,下面我們簡要介紹一下他們:

  1. FingerprintManager:主要用來協(xié)調(diào)管理和訪問指紋識別硬件設(shè)備
  2. FingerprintManager.AuthenticationCallback這個一個callback接口,當(dāng)指紋認(rèn)證后系統(tǒng)會回調(diào)這個接口通知app認(rèn)證的結(jié)果是什么
  3. FingerprintManager.AuthenticationResult這是一個表示認(rèn)證結(jié)果的類熄云,會在回調(diào)接口中以參數(shù)給出
  4. FingerprintManager.CryptoObject這是一個加密的對象類膨更,用來保證認(rèn)證的安全性,這是一個重點缴允,下面我們會分析荚守。
    好了珍德,到這里我們簡要知道了android 6.0給出的指紋識別的接口不是很多,可以說是簡短干練矗漾。

開始指紋驗證之前锈候,首先要確認(rèn)指紋是否支持指紋識別

主要是以下的幾個方面:

  1. 在AndroidManifest.xml中聲明權(quán)限
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
  1. API level 23 。

指紋識別API是在api level 23也就是android 6.0中加入的敞贡,因此我們的app必須運(yùn)行在這個系統(tǒng)版本之上泵琳。因此google推薦使用 Android Support Library v4包來獲得FingerprintManagerCompat對象,因為在獲得的時候這個包會檢查當(dāng)前系統(tǒng)平臺的版本誊役。

  1. 硬件

硬件識別要求設(shè)備(手機(jī))上有指紋識的硬件获列,因此在運(yùn)行中需要檢查當(dāng)前系統(tǒng)中是否有識別的硬件。

if (!fingerprintManager.isHardwareDetected()) {
    // no fingerprint sensor is detected, show dialog to tell user.
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(R.string.no_sensor_dialog_title);
    builder.setMessage(R.string.no_sensor_dialog_message);
    builder.setIcon(android.R.drawable.stat_sys_warning);
    builder.setCancelable(false);
    builder.setNegativeButton(R.string.cancel_btn_dialog, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();
        }
    });
    // show this dialog.
    builder.create().show();
}
  1. 當(dāng)前設(shè)備必須處在安全保護(hù)中蛔垢。

安全保護(hù)的意思指的是當(dāng)前設(shè)備必須使用屏幕鎖保護(hù)击孩,可以是password,可以是pin或者是圖形解鎖鹏漆。因為谷歌認(rèn)為當(dāng)前指紋解鎖還有一定的不足巩梢,仍然需要配合傳統(tǒng)的解鎖方式,使用以下代碼檢查:

KeyguardManager keyguardManager =(KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardSecure()) {
    // this device is secure.
}
  1. 系統(tǒng)中是不是有注冊的指紋艺玲。

普通app想要使用指紋識別功能括蝠,必須現(xiàn)在setting中注冊過一個指紋,否則不能使用饭聚,使用以下代碼進(jìn)行檢查:

if (!fingerprintManager.hasEnrolledFingerprints()) {
    // no fingerprint image has been enrolled.
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(R.string.no_fingerprint_enrolled_dialog_title);
    builder.setMessage(R.string.no_fingerprint_enrolled_dialog_message);
    builder.setIcon(android.R.drawable.stat_sys_warning);
    builder.setCancelable(false);
    builder.setNegativeButton(R.string.cancel_btn_dialog, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();
        }
    });
    // show this dialog
    builder.create().show();
}

目前谷歌對于所有普通app忌警,只允許對指紋進(jìn)行識別,并沒有提供指紋注冊的權(quán)限秒梳。

一個好的app應(yīng)該對以上的幾個條件進(jìn)行檢查慨蓝。


開始使用指紋識別

主要分以下幾個步驟:

  1. 申明權(quán)限
  2. 獲得FingerprintManager的對象引用
  3. 檢查指紋識別兼容性
  4. 掃描用戶指紋
  5. 處理驗證信息
  6. 其他(取消掃描)

1、申請權(quán)限

這個比較簡單

<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

2端幼、獲得FingerprintManager的對象引用

這個過程有兩種方式

// Using the Android Support Library v4
fingerprintManager = FingerprintManagerCompat.from(this);
// Using API level 23:
fingerprintManager = (FingerprintManager)getSystemService(Context.FINGERPRINT_SERVICE);

谷歌推行第一種使用方式,通過v4包獲得對象引用弧满。
第二種方式是使用api 23 framework中的接口獲得對象引用婆跑。

注:這里使用的是FingerprintManagerCompat而不是FingerprintManager。前者兼容后者庭呜,

3滑进、檢查運(yùn)行條件,主要指之前的一個板塊募谎。這里不多贅述

4扶关、掃描用戶指紋

這一步也比較簡單,只需要調(diào)用FingerprintManager的authenticate方法即可数冬。
看一下這個接口


authenticate接口

上圖是google的api文檔中的描述节槐,現(xiàn)在我們挨個解釋一下這些參數(shù)都是什么:

  1. crypto這是一個加密類的對象搀庶,指紋掃描器會使用這個對象來判斷認(rèn)證結(jié)果的合法性。這個對象可以是null铜异,但是這樣的話哥倔,就意味這app無條件信任認(rèn)證的結(jié)果,雖然從理論上這個過程可能被攻擊揍庄,數(shù)據(jù)可以被篡改咆蒿,這是app在這種情況下必須承擔(dān)的風(fēng)險。因此蚂子,建議這個參數(shù)不要置為null沃测。這個類的實例化有點麻煩,主要使用javax的security接口實現(xiàn)食茎,后面有一個例子可以參考
  2. cancel 這個是CancellationSignal類的一個對象蒂破,這個對象用來在指紋識別器掃描用戶指紋的是時候取消當(dāng)前的掃描操作,如果不取消的話董瞻,那么指紋掃描器會移植掃描直到超時(一般為30s寞蚌,取決于具體的廠商實現(xiàn)),這樣的話就會比較耗電钠糊。建議這個參數(shù)不要置為null挟秤。
  3. flags 標(biāo)識位,根據(jù)上圖的文檔描述抄伍,這個位暫時應(yīng)該為0艘刚,這個標(biāo)志位應(yīng)該是保留將來使用的。
  4. callback 這個是FingerprintManager.AuthenticationCallback類的對象截珍,這個是這個接口中除了第一個參數(shù)之外最重要的參數(shù)了攀甚。當(dāng)系統(tǒng)完成了指紋認(rèn)證過程(失敗或者成功都會)后,會回調(diào)這個對象中的接口岗喉,通知app認(rèn)證的結(jié)果秋度。這個參數(shù)不能為NULL。
  5. handler 這是Handler類的對象钱床,如果這個參數(shù)不為null的話荚斯,那么FingerprintManager將會使用這個handler中的looper來處理來自指紋識別硬件的消息。通常來講查牌,開發(fā)這不用提供這個參數(shù)事期,可以直接置為null,因為FingerprintManager會默認(rèn)使用app的main looper來處理纸颜。

5兽泣、處理驗證信息

在上一步中我們對指紋進(jìn)行了識別,接下來我們需要對callback結(jié)果進(jìn)行驗證胁孙。

實際上唠倦,這一步應(yīng)該在驗證之間進(jìn)行称鳞,首先是創(chuàng)建一個callback對象,這個對象中牵敷,對返回信息進(jìn)行重寫胡岔,然后直接進(jìn)行驗證,驗證結(jié)果就執(zhí)行我們重寫的內(nèi)容枷餐,達(dá)到處理驗證信息的目的靶瘸。

代碼如下:

FingerprintManagerCompat.AuthenticationCallback callback = new FingerprintManagerCompat.AuthenticationCallback(){

            @Override
            public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
                super.onAuthenticationSucceeded(result);
                Toast.makeText(context, "succeed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAuthenticationError(int errMsgId, CharSequence errString) {
                super.onAuthenticationError(errMsgId, errString);
                Toast.makeText(context, "error", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAuthenticationFailed() {
                super.onAuthenticationFailed();
                Toast.makeText(context, "failed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
                super.onAuthenticationHelp(helpMsgId, helpString);
                Toast.makeText(context, "help", Toast.LENGTH_SHORT).show();
            }
        };

上面的代碼也可以直接新建一個類,繼承FingerprintManagerCompat.AuthenticationCallback

callback接口

其中最主要的是這4個返回結(jié)果

  1. OnAuthenticationError(int errorCode, ICharSequence errString) 這個接口會再系統(tǒng)指紋認(rèn)證出現(xiàn)不可恢復(fù)的錯誤的時候才會調(diào)用毛肋,并且參數(shù)errorCode就給出了錯誤碼怨咪,標(biāo)識了錯誤的原因。這個時候app能做的只能是提示用戶重新嘗試一遍润匙。

  2. OnAuthenticationFailed() 這個接口會在系統(tǒng)指紋認(rèn)證失敗的情況的下才會回調(diào)诗眨。注意這里的認(rèn)證失敗和上面的認(rèn)證錯誤是不一樣的,雖然結(jié)果都是不能認(rèn)證孕讳。認(rèn)證失敗是指所有的信息都采集完整匠楚,并且沒有任何異常,但是這個指紋和之前注冊的指紋是不相符的厂财;但是認(rèn)證錯誤是指在采集或者認(rèn)證的過程中出現(xiàn)了錯誤芋簿,比如指紋傳感器工作異常等。也就是說認(rèn)證失敗是一個可以預(yù)期的正常情況璃饱,而認(rèn)證錯誤是不可預(yù)期的異常情況与斤。

  3. OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) 上面的認(rèn)證失敗是認(rèn)證過程中的一個異常情況,我們說那種情況是因為出現(xiàn)了不可恢復(fù)的錯誤荚恶,而我們這里的OnAuthenticationHelp方法是出現(xiàn)了可以回復(fù)的異常才會調(diào)用的撩穿。什么是可以恢復(fù)的異常呢?一個常見的例子就是:手指移動太快谒撼,當(dāng)我們把手指放到傳感器上的時候食寡,如果我們很快地將手指移走的話,那么指紋傳感器可能只采集了部分的信息廓潜,因此認(rèn)證會失敗冻河。但是這個錯誤是可以恢復(fù)的,因此只要提示用戶再次按下指紋茉帅,并且不要太快移走就可以解決。

  4. OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)這個接口會在認(rèn)證成功之后回調(diào)锭弊。我們可以在這個方法中提示用戶認(rèn)證成功堪澎。這里需要說明一下,如果我們上面在調(diào)用authenticate的時候味滞,我們的CryptoObject不是null的話樱蛤,那么我們在這個方法中可以通過AuthenticationResult來獲得Cypher對象然后調(diào)用它的doFinal方法钮呀。doFinal方法會檢查結(jié)果是不是會攔截或者篡改過,如果是的話會拋出一個異常昨凡。當(dāng)我們發(fā)現(xiàn)這些異常的時候都應(yīng)該將認(rèn)證當(dāng)做是失敗來來處理爽醋,為了安全建議大家都這么做。
    關(guān)于上面的接口還有2點需要補(bǔ)充一下:

  5. 上面我們說道OnAuthenticationError 和 OnAuthenticationHelp方法中會有錯誤或者幫助碼以提示為什么認(rèn)證不成功便脊。Android系統(tǒng)定義了幾個錯誤和幫助碼在FingerprintManager類中蚂四,如下:


    errorcodehelpcode

    我們的callback類實現(xiàn)的時候最好需要處理這些錯誤和幫助碼。

  6. 當(dāng)指紋掃描器正在工作的時候哪痰,如果我們?nèi)∠敬尾僮鞯脑捤煸到y(tǒng)也會回調(diào)OnAuthenticationError方法的,只是這個時候的錯誤碼是FingerprintManager.FINGERPRINT_ERROR_CANCELED(值為5)晌杰,因此app需要區(qū)別對待跷睦。

6、其他處理肋演。

上一步中抑诸,當(dāng)errorcode為5的時候,表示取消處理爹殊。這個處理是比較常見的操作蜕乡。直接使用CancellationSignal的cancel方法實現(xiàn)。
具體使用就是边灭,實力化一個CancellationSignal的對象异希,在認(rèn)證時,將該對象傳入绒瘦。需要取消的時候称簿,調(diào)用該對象的cancel方法就行了。

附:

創(chuàng)建CryptoObject類對象

上面我們分析FingerprintManager的authenticate方法的時候惰帽,看到這個方法的第一個參數(shù)就是CryptoObject類的對象憨降,現(xiàn)在我們看一下這個對象怎么去實例化。
我們知道该酗,指紋識別的結(jié)果可靠性是非常重要的授药,我們肯定不希望認(rèn)證的過程被一個第三方以某種形式攻擊,因為我們引入指紋認(rèn)證的目的就是要提高安全性呜魄。但是悔叽,從理論角度來說,指紋認(rèn)證的過程是可能被第三方的中間件惡意攻擊的爵嗅,常見的攻擊的手段就是攔截和篡改指紋識別器提供的結(jié)果娇澎。這里我們可以提供CryptoObject對象給authenticate方法來避免這種形式的攻擊。
FingerprintManager.CryptoObject是基于Java加密API的一個包裝類睹晒,并且被FingerprintManager用來保證認(rèn)證結(jié)果的完整性趟庄。通常來講括细,用來加密指紋掃描結(jié)果的機(jī)制就是一個Javax.Crypto.Cipher對象。Cipher對象本身會使用由應(yīng)用調(diào)用Android keystore的API產(chǎn)生一個key來實現(xiàn)上面說道的保護(hù)功能戚啥。
為了理解這些類之間是怎么協(xié)同工作的奋单,這里我給出一個用于實例化CryptoObject對象的包裝類代碼,我們先看下這個代碼是怎么實現(xiàn)的猫十,然后再解釋一下為什么是這樣览濒。

public class CryptoObjectHelper
{
    // This can be key name you want. Should be unique for the app.
    static final String KEY_NAME = "com.createchance.android.sample.fingerprint_authentication_key";

    // We always use this keystore on Android.
    static final String KEYSTORE_NAME = "AndroidKeyStore";

    // Should be no need to change these values.
    static final String KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
    static final String TRANSFORMATION = KEY_ALGORITHM + "/" +
    BLOCK_MODE + "/" +
    ENCRYPTION_PADDING;
    final KeyStore _keystore;

    public CryptoObjectHelper() throws Exception
    {
        _keystore = KeyStore.getInstance(KEYSTORE_NAME);
        _keystore.load(null);
    }

    public FingerprintManagerCompat.CryptoObject buildCryptoObject() throws Exception
    {
        Cipher cipher = createCipher(true);
        return new FingerprintManagerCompat.CryptoObject(cipher);
    }

    Cipher createCipher(boolean retry) throws Exception
    {
        Key key = GetKey();
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        try
        {
            cipher.init(Cipher.ENCRYPT_MODE | Cipher.DECRYPT_MODE, key);
        } catch(KeyPermanentlyInvalidatedException e)
        {
            _keystore.deleteEntry(KEY_NAME);
            if(retry)
            {
                createCipher(false);
            } else
            {
                throw new Exception("Could not create the cipher for fingerprint authentication.", e);
            }
        }
        return cipher;
    }

    Key GetKey() throws Exception
    {
        Key secretKey;
        if(!_keystore.isKeyEntry(KEY_NAME))
        {
            CreateKey();
        }

        secretKey = _keystore.getKey(KEY_NAME, null);
        return secretKey;
    }

    void CreateKey() throws Exception
    {
        KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME);
        KeyGenParameterSpec keyGenSpec =
                new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(BLOCK_MODE)
                        .setEncryptionPaddings(ENCRYPTION_PADDING)
                        .setUserAuthenticationRequired(true)
                        .build();
        keyGen.init(keyGenSpec);
        keyGen.generateKey();
    }
}

上面的類會針對每個CryptoObject對象都會新建一個Cipher對象,并且會使用由應(yīng)用生成的key炫彩。這個key的名字是使用KEY_NAME變量定義的匾七,這個名字應(yīng)該是保證唯一的,建議使用域名區(qū)別江兢。GetKey方法會嘗試使用Android Keystore的API來解析一個key(名字就是上面我們定義的)昨忆,如果key不存在的話,那就調(diào)用CreateKey方法新建一個key杉允。
cipher變量的實例化是通過調(diào)用Cipher.getInstance方法獲得的邑贴,這個方法接受一個transformation參數(shù),這個參數(shù)制定了數(shù)據(jù)怎么加密和解密叔磷。然后調(diào)用Cipher.init方法就會使用應(yīng)用的key來完成cipher對象的實例化工作拢驾。
這里需要強(qiáng)調(diào)一點,在以下情況下改基,android會認(rèn)為當(dāng)前key是無效的:

  1. 一個新的指紋image已經(jīng)注冊到系統(tǒng)中
  2. 當(dāng)前設(shè)備中的曾經(jīng)注冊過的指紋現(xiàn)在不存在了繁疤,可能是被全部刪除了
  3. 用戶關(guān)閉了屏幕鎖功能
  4. 用戶改變了屏幕鎖的方式
    當(dāng)上面的情況發(fā)生的時候,Cipher.init方法都會拋出KeyPermanentlyInvalidatedException的異常秕狰,上面我的代碼中捕獲了這個異常稠腊,并且刪除了當(dāng)前無效的key,然后根據(jù)參數(shù)嘗試再次創(chuàng)建鸣哀。
    上面的代碼中使用了android的KeyGenerator來創(chuàng)建一個key并且把它存儲在設(shè)備中架忌。KeyGenerator類會創(chuàng)建一個key,但是需要一些原始數(shù)據(jù)才能創(chuàng)建key我衬,這些原始的信息是通過KeyGenParameterSpec類的對象來提供的叹放。KeyGenerator類對象的實例化是使用它的工廠方法getInstance進(jìn)行的,從上面的代碼中我們可以看到這里使用的AES(Advanced Encryption Standard )加密算法的挠羔,AES會將數(shù)據(jù)分成幾個組井仰,然后針對幾個組進(jìn)行加密。
    接下來破加,KeyGenParameterSpec的實例化是使用它的Builder方法俱恶,KeyGenParameterSpec.Builder封裝了以下重要的信息:
  1. key的名字
  2. key必須在加密和解密的時候是有效的
  3. 上面代碼中BLOCK_MODE被設(shè)置為Cipher Block Chaining也就是KeyProperties.BLOCK_MODE_CBC,這意味著每一個被AES切分的數(shù)據(jù)塊都與之前的數(shù)據(jù)塊進(jìn)行了異或運(yùn)算了,這樣的目的就是為了建立每個數(shù)據(jù)塊之間的依賴關(guān)系速那。
  4. CryptoObjectHelper類使用了PKSC7(Public Key Cryptography Standard #7)的方式去產(chǎn)生用于填充AES數(shù)據(jù)塊的字節(jié),這樣就是要保證每個數(shù)據(jù)塊的大小是等同的(因為需要異或計算還有方面算法進(jìn)行數(shù)據(jù)處理尿背,詳細(xì)可以查看AES的算法原理)端仰。
  5. setUserAuthenticationRequired(true)調(diào)用意味著在使用key之前用戶的身份需要被認(rèn)證。
    每次KeyGenParameterSpec創(chuàng)建的時候田藐,他都被用來初始化KeyGenerator荔烧,這個對象會產(chǎn)生存儲在設(shè)備上的key。

比較簡單點理解就是汽久,指紋也是屬于密碼的一種鹤竭,需要我們對指紋的驗證過程進(jìn)行加密,而這個類就是用來對指紋的驗證過程加密景醇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臀稚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子三痰,更是在濱河造成了極大的恐慌吧寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件散劫,死亡現(xiàn)場離奇詭異稚机,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)获搏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門赖条,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人常熙,你說我怎么就攤上這事纬乍。” “怎么了症概?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵蕾额,是天一觀的道長。 經(jīng)常有香客問我彼城,道長诅蝶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任募壕,我火速辦了婚禮调炬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舱馅。我一直安慰自己缰泡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棘钞,像睡著了一般缠借。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宜猜,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天泼返,我揣著相機(jī)與錄音,去河邊找鬼姨拥。 笑死绅喉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叫乌。 我是一名探鬼主播柴罐,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼憨奸!你這毒婦竟也來了革屠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤膀藐,失蹤者是張志新(化名)和其女友劉穎屠阻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體额各,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡国觉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了虾啦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麻诀。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖傲醉,靈堂內(nèi)的尸體忽然破棺而出蝇闭,到底是詐尸還是另有隱情,我是刑警寧澤硬毕,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布呻引,位于F島的核電站,受9級特大地震影響吐咳,放射性物質(zhì)發(fā)生泄漏逻悠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一韭脊、第九天 我趴在偏房一處隱蔽的房頂上張望童谒。 院中可真熱鬧,春花似錦沪羔、人聲如沸饥伊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琅豆。三九已至愉豺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茫因,已是汗流浹背粒氧。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留节腐,地道東北人。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓摘盆,卻偏偏與公主長得像翼雀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子孩擂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容