移動(dòng)端系統(tǒng)生物認(rèn)證技術(shù)詳解

相信大家對(duì)于生物認(rèn)證應(yīng)該不會(huì)陌生,使用指紋登陸或者 FaceId 支付等的需求場(chǎng)景如今已經(jīng)很普遍,所以基本上只要涉及移動(dòng)端開發(fā),不管是 Android 、iOS 或者是 RN 、Flutter 都多多少少會(huì)接觸到這一業(yè)務(wù)場(chǎng)景。

當(dāng)然,不同之處可能在于大家對(duì)于平臺(tái)能力或者接口能力的熟悉程度,所以本篇主要介紹 Android 和 iOS 上使用系統(tǒng)的生物認(rèn)證需要注意什么,具體流程是什么,給需要或者即將需要的大家出一份匯總的資料

??注意:本篇更傾向于調(diào)研資料的角度,適合需要接入或者在接入過程中出現(xiàn)疑問的方向,而不是 API 使用教程,另外篇幅較長(zhǎng)警告~

首先,先簡(jiǎn)單說一個(gè)大家都知道的概念,那就是不管是 Android 或者 iOS ,不管是指紋還是 FaceId ,只要使用的是系統(tǒng)提供的 API ,作為開發(fā)者是拿不到任何用戶的生物特征數(shù)據(jù),所以簡(jiǎn)單來說你只能調(diào)用系統(tǒng) API ,然后得到成功或者失敗的結(jié)果

一、Android

Android 上的生物認(rèn)證發(fā)展史可以說是十分崎嶇,目前簡(jiǎn)單來說經(jīng)歷了兩個(gè)階段:

  • FingerprintManager (API 23)
  • BiometricPrompt(API 28)

所以如下圖所示,你會(huì)看到其實(shí)底層有兩套 Service 在支持生物認(rèn)證的 API 能力,但是值得注意的是, FingerprintManager 在 Api28(Android P)被添加了 @Deprecated 標(biāo)記 ,包括 androidx 里的兼容包 FingerprintManagerCompat 也是被標(biāo)注了 @Deprecated ,因?yàn)楣俜教峁└倒鲜剑_箱即用的 androidx.biometrics.BiometricPrompt

1.1、使用 BiometricPrompt

簡(jiǎn)單介紹下接入 BiometricPrompt ,首先第一步是添加權(quán)限


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.biometric">
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />

</manifest>

接著調(diào)用 BiometricPrompt 構(gòu)建系統(tǒng)彈出框信息,具體內(nèi)容對(duì)應(yīng)可見下圖:

[圖片上傳失敗...(image-a9ca67-1648602602525)]

最用設(shè)置 AuthenticationCallback 和調(diào)用 authenticate ,然后等待授權(quán)結(jié)果進(jìn)入到成功的回調(diào):

    biometricPrompt = new BiometricPrompt(activity, uiThreadExecutor, authenticationCallback);
    biometricPrompt.authenticate(promptInfo);

當(dāng)然上述代碼還少了很多細(xì)節(jié):

  • 比如需要是 FragmentActivity ;

  • 檢測(cè)設(shè)備是否支持生物認(rèn)證(還有不支持的現(xiàn)在?);

  • 判斷支持哪種生物認(rèn)證,當(dāng)然默認(rèn) BiometricPrompt 會(huì)幫你處理,如果有多種會(huì)彈出選擇;

而認(rèn)證不成功的時(shí)候可以在 onAuthenticationError 里獲取到對(duì)應(yīng)的錯(cuò)誤碼:

onAuthenticationError Type
BIOMETRIC_ERROR_LOCKOUT 操作被取消,因?yàn)?API 由于嘗試次數(shù)過多而被鎖定(一般就是在一次 authenticate 里例如多次指紋沒通過,鎖定了, 但是過一會(huì)還可以調(diào)用)
BIOMETRIC_ERROR_LOCKOUT_PERMANENT 由于 BIOMETRIC_ERROR_LOCKOUT 發(fā)生太多次,操作被取消,這個(gè)就是真的 LOCK 了。
BIOMETRIC_ERROR_NO_SPACE 剩余存儲(chǔ)空間不足
BIOMETRIC_ERROR_TIMEOUT 超時(shí)
BIOMETRIC_ERROR_UNABLE_TO_PROCESS 傳感器異常或者無法處理當(dāng)前信息
BIOMETRIC_ERROR_USER_CANCELED 用戶取消了操作
BIOMETRIC_ERROR_NO_BIOMETRIC 用戶沒有在設(shè)備中注冊(cè)任何生物特征
BIOMETRIC_ERROR_CANCELED 由于生物傳感器不可用,操作被取消
BIOMETRIC_ERROR_HW_NOT_PRESENT 設(shè)備沒有生物識(shí)別傳感器
BIOMETRIC_ERROR_HW_UNAVAILABLE 設(shè)備硬件不可用
BIOMETRIC_ERROR_VENDOR 如果存在不屬于上述之外的情況,Other

1.2、BiometricPrompt 自定義

簡(jiǎn)單接入完 BiometricPrompt 之后, 你可能會(huì)有個(gè)疑問: BiometricPrompt 是很方便,但是 UI 有點(diǎn)丑了,可以自定義嗎?

抱歉,不可以 ,是的,BiometricPrompt不能自定義 UI,甚至你想改個(gè)顏色都“費(fèi)勁”, 如果你去看 biometric 的源碼,就會(huì)發(fā)現(xiàn)官方并沒有讓你自定義的打算,除非你 cv 這些代碼自己構(gòu)建一套,至于為什么會(huì)有這樣的設(shè)計(jì),我個(gè)人猜測(cè)其中一條就是屏下指紋

在官方的 《Migrating from FingerprintManager to BiometricPrompt》里也說了:丟棄指紋的布局文件,因?yàn)槟銓⒉辉傩枰鼈儯珹ndroidX 生物識(shí)別庫帶有標(biāo)準(zhǔn)化的 UI。

什么是標(biāo)準(zhǔn)化的 UI ?如下所示是使用 BiometricPrompt 的三臺(tái)手機(jī),可以看到:

  • 第一和第二臺(tái)除了位置有些許不同,其他基本一致;
  • 第三胎手機(jī)是屏下指紋,可以看到整個(gè)指紋輸入的 UI 效果完全是廠家自己的另外一種風(fēng)格;

所以使用 BiometricPrompt 你將不需要關(guān)注 UI 問題,因?yàn)槟銢]得選,甚至你也不需要關(guān)注手機(jī)上的生物認(rèn)證類型的安全度問題,因?yàn)椴还苁? CDD 還是 UI ,OEM 廠商的都會(huì)直接實(shí)現(xiàn)好,例如三星的 UI 是如下圖所示:

Android 兼容性定義文檔 (Android CDD)_里描述了生物認(rèn)證傳感器安全度的強(qiáng)弱,而在 framework 層面 BiometricFragmentFingerprintDialogFragment 都是 @hide ,甚至你單純?nèi)シ?androidx.biometric:biometric.aar 的庫,你都看不到 BiometricFragment 的布局,只能看到 FingerprintDialogFragment 的 layout。

那就沒辦法自定義 UI 了嗎?還是有的,有兩個(gè)選擇:

  • 繼續(xù)使用 FingerprintManager ,雖然標(biāo)注了棄用,但是目前還是可以用,在 Android 11 上也可以正常執(zhí)行對(duì)應(yīng)邏輯,下圖是同一臺(tái)手機(jī)在 Android 11 上使用 FingerprintManagerBiometricPrompt 的對(duì)比:
  • 使用騰訊的 soter ,這個(gè)我們后面講;

1.3、Login + BiometricPrompt

介紹完調(diào)用和 UI ,那就再結(jié)合 Login 場(chǎng)景聊聊 BiometricPrompt ,官方針對(duì) Login 場(chǎng)景提供了一個(gè) Demo ,這里主要介紹整個(gè)業(yè)務(wù)流程,具體代碼可以看官方的 BiometricLoginKotlin ,前面說過生物認(rèn)證只提供認(rèn)證結(jié)果,那么結(jié)合 Login 業(yè)務(wù),在官方的例子中 BiometricPrompt 主要是用于做認(rèn)證和加密的作用

如上圖所示,場(chǎng)景是在登陸之后,我們獲取到了用戶的 Token 信息,這個(gè) Token 信息可能是服務(wù)器基于用戶密碼合并后的內(nèi)容,所以它包含了一些敏感隱私,為了安全期間我們不能直接存儲(chǔ),而是利用 BiometricPrompt 去實(shí)現(xiàn)加密后存儲(chǔ):

  • 首先通過 KeyStore,主要是得到一個(gè)包含密碼的 SecretKey ,當(dāng)然這里有一個(gè)關(guān)鍵操作,那就是 setUserAuthenticationRequired(true),后面我們?cè)俳忉專?/li>
  • 然后利用 SecretKey 創(chuàng)建 ClipherClipher 就是 Java 里常用于加解密的對(duì)象;
  • 利用 BiometricPrompt.CryptoObject(cipher) 去調(diào)用生物認(rèn)證授權(quán);
  • 授權(quán)成功后會(huì)得到一個(gè) AuthenticationResult ,Result 里面包含存在密鑰信息的 cryptoObject?.ciphercipher.iv 加密偏移向量;
  • 利用授權(quán)成功后的 cryptoObject?.cipher 對(duì) Token 進(jìn)行加密,然后和 cipher.iv 一起保存到 SharePerferences ,就完成了基于 BiometricPrompt 的加密保存;

是不是覺得有點(diǎn)懵? 簡(jiǎn)單說就是:我們通過一個(gè)只有用戶通過身份驗(yàn)證時(shí)才授權(quán)使用的密鑰來加密 Token ,這樣不管這個(gè) Token 是否泄漏,對(duì)于我們來說都是安全的。

然后在 KeyStore 邏輯里這里有個(gè) setUserAuthenticationRequired(true) 操作,這個(gè)操作的意思就是:是否僅在用戶通過身份驗(yàn)證時(shí)才授權(quán)使用此密鑰,也就是當(dāng)設(shè)置為 true 時(shí):

用戶必須通過使用其鎖屏憑據(jù)的子集(例如密碼/PIN/圖案或生物識(shí)別)向此 Android 設(shè)備進(jìn)行身份驗(yàn)證,才能夠而授權(quán)使用密鑰。

也就是只有設(shè)置了安全鎖屏?xí)r才能生成密鑰,而一旦安全鎖屏被禁用(重新配置為無、不驗(yàn)證用戶身份的模式、被強(qiáng)制重置)時(shí),密鑰將不可逆轉(zhuǎn)地失效。

另外可以設(shè)置了 setUserAuthenticationValidityDurationSeconds 來要求密鑰必須至少有一個(gè)生物特征才可用,而一但它設(shè)置為 true,如果用戶注冊(cè)了新的生物特征,它也將不可逆轉(zhuǎn)地失效。

所以可以看到,這個(gè)流程下密鑰會(huì)和系統(tǒng)安全綁定到一起,從而不害怕 Token 等信息的泄漏,也因?yàn)槭跈?quán)成功后的 CryptoObjectKeyStore 集成到一起,可以更有效地抵御例如 root 的攻擊。

而反之獲取的流程也是類似,如下圖所示:

  • SharePerferences 里獲取加密后的 Token 和 iv 信息;
  • 同樣是利用 SecretKey 創(chuàng)建 Clipher ,不過這次要帶上保存的 iv 信息;
  • 利用 BiometricPrompt.CryptoObject(cipher) 去調(diào)用生物認(rèn)證授權(quán);
  • 通過授權(quán)成功后的 cryptoObject?.cipher 對(duì) Token 進(jìn)行加密,得到原始的 Token 信息;

所以可以看到,基本思路就是利用 BiometricPrompt 認(rèn)證后得到 CryptoObject?.Cipher 去加解密,通過系統(tǒng)的安全等級(jí)要保護(hù)我們的隱私信息

最后補(bǔ)充一個(gè)知識(shí)點(diǎn),雖然一般我們不關(guān)心,但是在 BiometricPrompt 里有 auth-per-usetime-bound 這兩個(gè)概念:

  • auth-per-use 密鑰要求每次使用密鑰時(shí),都必須進(jìn)行認(rèn)證 ,前面我們通過 BiometricPrompt.CryptoObject(cipher) 去調(diào)用授權(quán)方法就是這類實(shí)現(xiàn);
  • time-bound 密鑰是一種在一定的時(shí)間段內(nèi)有效的密鑰,可以通過 setUserAuthenticationValidityDurationSeconds 設(shè)置有效時(shí)長(zhǎng),如果你設(shè)置為很短,例如 5 秒,那行為上和 auth-per-use 基本類似;

更多資料可以參考官方的 biometric-authentication-on-android

1.4、Tencent soter

前面說到 Android 上還有 soter ,騰訊在微信指紋支付全流程之上,將它的流程抽象為一套完備的生物識(shí)別標(biāo)準(zhǔn):SOTER。

SOTER 會(huì)與手機(jī)廠商合作,在系統(tǒng)原有的接口能力之上提供安全加固,通過業(yè)務(wù)無關(guān)的安全域(TEE,即獨(dú)立于手機(jī)操作系統(tǒng)的安全區(qū)域,root或越獄無法訪問到)應(yīng)用程序(TA)降低開發(fā)難度和適配成本,做到即使外部環(huán)境不可信,依然可以安全授權(quán)。

TEE(Trusted Execution Environment)是獨(dú)立于手機(jī)操作系統(tǒng)的一塊獨(dú)立運(yùn)行的安全區(qū)域,SOTER標(biāo)準(zhǔn)中,所有的密鑰生成、數(shù)據(jù)簽名處理、指紋驗(yàn)證、敏感數(shù)據(jù)傳輸?shù)让舾胁僮骶?TEE 中進(jìn)行,并且 SOTER使用的設(shè)備根密鑰由廠商在產(chǎn)線上燒入,從根本上解決了根密鑰不可信的問題,并以此根密鑰為信任鏈根,派生密鑰,從而完成,與微信合作的所有手機(jī)廠商將均帶有硬件TEE,并且通過騰訊安全平臺(tái)和微信支付安全團(tuán)隊(duì)驗(yàn)收,符合SOTER標(biāo)準(zhǔn)。

簡(jiǎn)而言之,這是一個(gè)支持直通廠商,并且具備后臺(tái)服務(wù)對(duì)接校驗(yàn)的第三方庫,目前最近 5 個(gè)月都還有在更新,那它有什么問題呢?

那就是必須是與微信合作的所有手機(jī)廠商和機(jī)型才能正常使用 ,而且經(jīng)常在一些廠商系統(tǒng)上出現(xiàn)奇奇怪怪的問題,比如:

  • MiUI13 綁定服務(wù)異常;
  • 鴻蒙系統(tǒng)API層面報(bào)錯(cuò);
  • 莫名其妙地出現(xiàn)崩潰;

但是它可以實(shí)現(xiàn)基本類似于微信支付的能力,所以如何取舍就看你的業(yè)務(wù)需求了。

支持機(jī)型可查閱 :#有多少設(shè)備已經(jīng)支持tencent-soter

iOS

相對(duì)來說 iOS 上的生物認(rèn)證就舒適不少,相比較 Android 上需要區(qū)分系統(tǒng)版本和廠商的 fingerprintfaceiris ,iOS 上的 Face ID 和 Touch ID 就十分統(tǒng)一和簡(jiǎn)潔。

簡(jiǎn)單介紹下 iOS 上使用生物認(rèn)證,首先需要在 Info.plist 文件添加描述信息:

<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>

然后導(dǎo)入頭文件 #import <LocalAuthentication/LocalAuthentication.h> ,最后創(chuàng)建 LAContext 去執(zhí)行授權(quán)操作,這里也簡(jiǎn)單展示對(duì)應(yīng)的錯(cuò)誤碼:

Error Code Type
LAErrorSystemCancel 系統(tǒng)取消了授權(quán),比如有其他APP切換
LAErrorUserCancel 用戶取消驗(yàn)證
LAErrorAuthenticationFailed 授權(quán)失敗
LAErrorPasscodeNotSet 系統(tǒng)未設(shè)置密碼
LAErrorBiometryNotAvailable ID不可用,例如未打開
LAErrorBiometryNotEnrolled ID不可用,用戶未錄入
LAErrorUserFallback 用戶選擇輸入密碼

而同樣關(guān)于自定義 UI 問題上,想必大家都知道了,iOS 生物認(rèn)證沒有自定義 UI 的說法,也不支持自定義 UI ,系統(tǒng)怎么樣就怎么樣,你可以做的只有類似配置‘是否允許使用密碼授權(quán)’這樣的行為

在這一點(diǎn)上相信 Android 開發(fā)都十分羨慕 iOS ,有問題也是系統(tǒng)問題,無法修復(fù)。

同樣,簡(jiǎn)單說說在 iOS 上使用生物識(shí)別的 Login 場(chǎng)景流程:

  • 獲取到 Token 信息后,驗(yàn)證用戶的 TouchID/FaceID ;
  • 驗(yàn)證通過后,將 Token 等信息保存到 keychain (keychain 只是一個(gè)數(shù)據(jù)存儲(chǔ),用于存儲(chǔ)一些敏感數(shù)據(jù)如密碼、證書等);
  • 保存成功后,下次再次登錄時(shí)通過驗(yàn)證 TouchID/FaceID 獲取對(duì)應(yīng)信息;

這里主要有兩個(gè)關(guān)鍵點(diǎn):

  • 訪問級(jí)別 : 例如是否需要每次都進(jìn)行身份驗(yàn)證時(shí)才可以訪問項(xiàng)目;
  • 身份驗(yàn)證級(jí)別: 也就是什么場(chǎng)景下可以訪問到存儲(chǔ)的信息;

舉個(gè)例子,訪問 keychain 首先是需要?jiǎng)?chuàng)建 accessControl ,一般可以通過 SecAccessControlCreateWithFlags 來創(chuàng)建 accessControl ,這里有個(gè)關(guān)鍵參數(shù)用于指定訪問級(jí)別:

  • kSecAttrAccessibleAfterFirstUnlock 開機(jī)之后密鑰不可用,需要等用戶輸入開機(jī)密碼
  • kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: 開機(jī)之后密鑰不可用,需要等用戶輸入開機(jī)密碼,但是僅限于當(dāng)前設(shè)備
  • kSecAttrAccessibleWhenUnlocked: 解鎖過的設(shè)備密鑰會(huì)保持可用狀態(tài)
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly: 解鎖過的設(shè)備密鑰會(huì)保持可用狀態(tài),僅當(dāng)前設(shè)備
  • kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: 解鎖過的設(shè)備密鑰會(huì)保持可用狀態(tài),只有用戶設(shè)置密碼后密鑰才可用
  • kSecAttrAccessibleAlways: 始終可用,已經(jīng) Deprecated
  • kSecAttrAccessibleAlwaysThisDeviceOnly: 密鑰始終可用,但無法遷移到其他設(shè)備,已經(jīng) Deprecated

類似場(chǎng)景下一般使用 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly ,另外還有 SecAccessControlCreateFlags標(biāo)志,它主要是用于指定希望用戶在訪問鑰匙串時(shí)的約束,一般類似場(chǎng)景會(huì)使用 userPresence

  • devicePasscode: 限制使用密碼訪問
  • biometryAny: 使用任何已注冊(cè) touch 或 face ID 訪問
  • biometryCurrentSet: 限制使用當(dāng)前注冊(cè) touch 或 face ID 訪問
  • userPresence: 限制使用生物特征或密碼訪問
  • watch: 使用手表訪問

創(chuàng)建完成 accessControl 之后,通過設(shè)置 kSecAttrAccessControl 后正常把信息存儲(chǔ)到 keychain 就可以了,在存儲(chǔ) keychain 時(shí)也有可選的 kSecClass ,一般選用 kSecClassGenericPassword

  • kSecClassGenericPassword: 通用密碼
  • kSecClassInternetPassword: Internet 密碼
  • kSecClassCertificate:證書
  • kSecClassKey:加密密鑰
  • kSecClassIdentity: 身份認(rèn)證

當(dāng)然,此時(shí)你是否發(fā)現(xiàn),在談及 accessControl 和 keychain 時(shí)沒有說明 LAContext

其實(shí)在創(chuàng)建 accessControl 時(shí)是有對(duì)應(yīng) kSecUseAuthenticationContext 參數(shù)用于設(shè)置 LAContext 到 keychain 認(rèn)證,但是也可以不設(shè)置,具體為:

  • 如果未指定,并且該項(xiàng)目需要 authentication 認(rèn)證,那就會(huì)自動(dòng)創(chuàng)建一個(gè)新的 LAContext ,使用一次后丟棄;
  • 如果是使用先前已通過身份驗(yàn)證的 LAContext ,則操作直接成功而不要求用戶進(jìn)行身份驗(yàn)證;
  • 如果是使用先前未經(jīng)過身份驗(yàn)證的 LAContext ,則系統(tǒng)會(huì)嘗試在該 LAContext 上進(jìn)行身份驗(yàn)證,如果成功就可以在后續(xù)的鑰匙串操作中重用。

更多可見官方的: accessing_keychain_items_with_face_id_or_touch_id

可以看到, iOS 上都只需要簡(jiǎn)單地配置就行了,因?yàn)橄到y(tǒng)層面也不會(huì)給你多余的能力。

三、最后

雖然本篇從頭到位并沒有教你如何使用 Android 或者 iOS 的生物認(rèn)證,但是作為匯總資料,本篇基本覆蓋了 Android 或者 iOS 生物認(rèn)證相關(guān)的基本概念和問題,相信本篇將會(huì)特別適合正在調(diào)研生物認(rèn)證相關(guān)開發(fā)的小伙伴。

最后,還是慣例,如果對(duì)于這方便你有什么問題或者建議,歡迎留言評(píng)論交流。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

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