AFNetworking源碼探究(十三) —— AFSecurityPolicy與安全認證 (一)

版本記錄

版本號 時間
V1.0 2018.03.01

前言

我們做APP發起網絡請求,都離不開一個非常有用的框架AFNetworking,可以說這個框架的知名度已經超過了蘋果的底層網絡請求部分,很多人可能不知道蘋果底層是如何發起網絡請求的,但是一定知道AFNetworking,接下來幾篇我們就一起詳細的解析一下這個框架。感興趣的可以看上面寫的幾篇。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請求實現之NSURLSessionDataTask實例化(一)
3. AFNetworking源碼探究(三) —— GET請求實現之任務進度設置和通知監聽(一)
4. AFNetworking源碼探究(四) —— GET請求實現之代理轉發思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個轉發代理方法詳細解析(一)
10. AFNetworking源碼探究(十) —— 數據解析之數據解析架構的分析(一)
11. AFNetworking源碼探究(十一) —— 數據解析之子類中協議方法的實現(二)
12. AFNetworking源碼探究(十二) —— 數據解析之子類中協議方法的實現(三)

回顧

前面講述了AFN中數據的解析機制,這一篇看一下AFN與HTTPS認證。


HTTPS

以下內容來自百度

HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。 它是一個URI scheme(抽象標識符體系),句法類同http:體系。用于安全的HTTP數據傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默認端口及一個加密/身份驗證層(在HTTP與TCP之間)。這個系統的最初研發由網景公司(Netscape)進行,并內置于其瀏覽器Netscape Navigator中,提供了身份驗證與加密通訊方法。

1. HTTP和HTTPS區別

超文本傳輸協議HTTP協議被用于在Web瀏覽器和網站服務器之間傳遞信息。HTTP協議以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了Web瀏覽器和網站服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此HTTP協議不適合傳輸一些敏感信息,比如信用卡號、密碼等。

為了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文本傳輸協議HTTPS。為了數據傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,并為瀏覽器和服務器之間的通信加密。

HTTPS和HTTP的區別主要為以下四點:

  • https協議需要到ca申請證書,一般免費證書很少,需要交費。
  • http是超文本傳輸協議,信息是明文傳輸,https 則是具有安全性ssl加密傳輸協議。
  • http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
  • http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

2. 解決問題

  • 信任主機的問題
  • 通訊過程中的數據的泄密和被篡改

3. SSL

SSL(Secure Sockets Layer安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是為網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密。

SSL (Secure Socket Layer)為Netscape所研發,用以保障在Internet上數據傳輸之安全,利用數據加密(Encryption)技術,可確保數據在網絡上之傳輸過程中不會被截取及竊聽。

SSL協議位于TCP/IP協議與各種應用層協議之間,為數據通訊提供安全支持。SSL協議可分為兩層:SSL記錄協議(SSL Record Protocol):它建立在可靠的傳輸協議(如TCP)之上,為高層協議提供數據封裝、壓縮、加密等基本功能的支持。SSL握手協議(SSL Handshake Protocol):它建立在SSL記錄協議之上,用于在實際的數據傳輸開始前,通訊雙方進行身份認證、協商加密算法、交換加密密鑰等。

SSL協議提供的服務主要有哪些

  • 認證用戶和服務器,確保數據發送到正確的客戶機和服務器
  • 加密數據以防止數據中途被竊取
  • 維護數據的完整性,確保數據在傳輸過程中不被改變。

SSL協議的工作流程

  • 服務器認證階段:

    • 客戶端向服務器發送一個開始信息“Hello”以便開始一個新的會話連接;
    • 服務器根據客戶的信息確定是否需要生成新的主密鑰,如需要則服務器在響應客戶的“Hello”信息時將包含生成主密鑰所需的信息;
    • 客戶根據收到的服務器響應信息,產生一個主密鑰,并用服務器的公開密鑰加密后傳給服務器;
    • 服務器恢復該主密鑰,并返回給客戶一個用主密鑰認證的信息,以此讓客戶認證服務器。
  • 用戶認證階段:

    • 在此之前,服務器已經通過了客戶認證,這一階段主要完成對客戶的認證。經認證的服務器發送一個提問給客戶,客戶則返回(數字)簽名后的提問和其公開密鑰,從而向服務器提供認證。

從SSL 協議所提供的服務及其工作流程可以看出,SSL協議運行的基礎是商家對消費者信息保密的承諾,這就有利于商家而不利于消費者。在電子商務初級階段,由于運作電子商務的企業大多是信譽較高的大公司,因此這問題還沒有充分暴露出來。但隨著電子商務的發展,各中小型公司也參與進來,這樣在電子支付過程中的單一認證問題就越來越突出。雖然在SSL3.0中通過數字簽名數字證書可實現瀏覽器和Web服務器雙方的身份驗證,但是SSL協議仍存在一些問題,比如,只能提供交易中客戶與服務器間的雙方認證,在涉及多方的電子交易中,SSL協議并不能協調各方間的安全傳輸和信任關系。在這種情況下,Visa和MasterCard兩大信用卡公組織制定了SET協議,為網上信用卡支付提供了全球性的標準。

4. 握手過程

為了便于更好的認識和理解SSL 協議,這里著重介紹SSL 協議的握手協議。SSL 協議既用到了公鑰加密技術又用到了對稱加密技術,對稱加密技術雖然比公鑰加密技術的速度快,可是公鑰加密技術提供了更好的身份認證技術。SSL 的握手協議非常有效的讓客戶和服務器之間完成相互之間的身份認證,其主要過程如下:

①客戶端的瀏覽器向服務器傳送客戶端SSL 協議的版本號,加密算法的種類,產生的隨機數,以及其他服務器和客戶端之間通訊所需要的各種信息。

②服務器向客戶端傳送SSL 協議的版本號,加密算法的種類,隨機數以及其他相關信息,同時服務器還將向客戶端傳送自己的證書。

③客戶利用服務器傳過來的信息驗證服務器的合法性,服務器的合法性包括:證書是否過期,發行服務器證書的CA 是否可靠,發行者證書的公鑰能否正確解開服務器證書的“發行者的數字簽名”,服務器證書上的域名是否和服務器的實際域名相匹配。如果合法性驗證沒有通過,通訊將斷開;如果合法性驗證通過,將繼續進行第四步。

④用戶端隨機產生一個用于后面通訊的“對稱密碼”,然后用服務器的公鑰(服務器的公鑰從步驟②中的服務器的證書中獲得)對其加密,然后將加密后的“預主密碼”傳給服務器。

⑤如果服務器要求客戶的身份認證(在握手過程中為可選),用戶可以建立一個隨機數然后對其進行數據簽名,將這個含有簽名的隨機數和客戶自己的證書以及加密過的“預主密碼”一起傳給服務器。

⑥如果服務器要求客戶的身份認證,服務器必須檢驗客戶證書和簽名隨機數的合法性,具體的合法性驗證過程包括:客戶的證書使用日期是否有效,為客戶提供證書的CA 是否可靠,發行CA 的公鑰能否正確解開客戶證書的發行CA 的數字簽名,檢查客戶的證書是否在證書廢止列表(CRL)中。檢驗如果沒有通過,通訊立刻中斷;如果驗證通過,服務器將用自己的私鑰解開加密的“預主密碼”,然后執行一系列步驟來產生主通訊密碼(客戶端也將通過同樣的方法產生相同的主通訊密碼)。

⑦服務器和客戶端用相同的主密碼即“通話密碼”,一個對稱密鑰用于SSL 協議的安全數據通訊的加解密通訊。同時在SSL 通訊過程中還要完成數據通訊的完整性,防止數據通訊中的任何變化。

客戶端服務器端發出信息,指明后面的數據通訊將使用的步驟⑦中的主密碼為對稱密鑰,同時通知服務器客戶端的握手過程結束。

⑨服務器向客戶端發出信息,指明后面的數據通訊將使用的步驟⑦中的主密碼為對稱密鑰,同時通知客戶端服務器端的握手過程結束。

⑩SSL 的握手部分結束,SSL 安全通道的數據通訊開始,客戶和服務器開始使用相同的對稱密鑰進行數據通訊,同時進行通訊完整性的檢驗。

5. SSL證書包含的信息

  • 證書版本號,不同版本的證書格式不同
  • Serial Number 序列號,同一身份驗證機構簽發的證書序列號唯一
  • Algorithm Identifier  簽名算法,包括必要的參數Issuer 身份驗證機構的標識信息
  • Period of Validity  有效期
  • Subject 證書持有人的標識信息
  • Subject’s Public Key 證書持有人的公鑰
  • Signature 身份驗證機構對證書的簽名
  • 證書的格式  認證中心所發放的證書均遵循X.509 V3 標準,其基本格式如下:
  • 證書版本號(Certificate Format Version)
    • 含義:用來指定證書格式采用的X.509 版本號。
  • 證書序列號(Certificate Serial Number)
    • 含義:用來指定證書的唯一序列號,以標識CA 發出的所有公鑰證書。
  • 簽名(Signature)算法標識(Algorithm Identifier)
    • 含義:用來指定 CA 簽發證書所用的簽名算法。
  • 簽發此證書的 CA 名稱(Issuer )
    • 含義:用來指定簽發證書的 CA 的X.500 唯一名稱(DN,Distinguished Name)。
  • 證書有效期(Validity Period)起始日期(notBefore) 終止日期(notAfter)
    • 含義:用來指定證書起始日期和終止日期。
  • 用戶名稱(Subject)
    • 含義:用來指定證書用戶的X.500 唯一名稱(DN,Distinguished Name)。
  • 用戶公鑰信息(Subject Public Key Information)算法(algorithm) 算法標識(Algorithm Identifier)用戶公鑰(subject Public Key)
    • 含義:用來標識公鑰使用的算法,并包含公鑰本身。
  • 證書擴充部分(擴展域)(Extensions)
    • 含義:用來指定額外信息。

X.509 V3 證書的擴充部分(擴展域)及實現方法如下:

  • CA 的公鑰標識(Authority Key Identifier)
  • 公鑰標識(SET 未使用)(Key Identifier)
  • 簽發證書者證書的簽發者的甄別名(Certificate Issuer)
  • 簽發證書者證書的序列號(Certificate Serial Number)

X.509 V3 證書的擴充部分(擴展域)及實現CA 的公鑰標識(Authority Key Identifier)

  • 公鑰標識(SET 未使用)(Key Identifier)
  • 簽發證書者證書的簽發者的甄別名(Certificat簽發證書者證書的序列號(Certificate Serial Number)
    • 含義:CA 簽名證書所用的密鑰對的唯一標識用戶的公鑰標識(Subject Key Identifier)
    • 含義:用來標識與證書中公鑰相關的特定密鑰進行解密。
  • 證書中的公鑰用途(Key Usage)
    • 含義:用來指定公鑰用途。
  • 用戶的私鑰有效期(Private Key Usage Period)起始日期(Note Before) 終止日期(Note After)
    • 含義:用來指定用戶簽名私鑰的起始日期和終止日期。
  • CA 承認的證書政策列表(Certificate Policies)
    • 含義:用來指定用戶證書所適用的政策,證書政策可由對象標識符表示。
  • 用戶的代用名(Substitutional Name)
    • 含義:用來指定用戶的代用名。
  • CA 的代用名(Issuer Alt Name)
    • 含義:用來指定 CA 的代用名。
  • 基本制約(Basic Constraints)
    • 含義:用來表明證書用戶是最終用戶還是CA。 在SET 系統中有一些私有擴充部分(擴展域)Hashed Root Key 含義:只在根證書中使用,用于證書更新時進行回溯。
  • 證書類型(Certificate Type)
    • 含義:用來區別不同的實體。該項是必選的。
  • 商戶數據(Merchant Data)
    • 含義:包含支付網關需要的所有商戶信息。
  • 持卡人證書需求(Card Cert Required)
    • 含義:顯示支付網關是否支持與沒有證書的持卡人進行交易。
  • SET 擴展(SETExtensions)
    • 含義:列出支付網關支持的支付命令的 SET 信息擴展。
  • CRL 數據定義版本(Version)
    • 含義:顯示 CRL 的版本號。
  • CRL 的簽發者(Issuer)
    • 含義:指明簽發 CRL 的CA 的甄別名。

CRL 發布時間(this Update)預計下一個 CRL 更新時間(Next Update)撤銷證書信息目錄(Revoked Certificates)CRL 擴展(CRL Extension)CA 的公鑰標識(Authority Key Identifier)CRL 號(CRL Number)

SSL證書種類

CFCA,GlobalSign,VeriSign ,Geotrust ,Thawte。

  • 域名型 https 證書(DVSSL):信任等級一般,只需驗證網站的真實性便可頒發證書保護網站;
  • 企業型 https 證書(OVSSL):信任等級強,須要驗證企業的身份,審核嚴格,安全性更高;
  • 增強型 https 證書(EVSSL):信任等級最高,一般用于銀行證券等金融機構,審核嚴格,安全性最高,同時可以激活綠色網址欄。

HTTPS的認證過程

1. 單向認證

這個認證過程可以參考下圖

  • 客戶端發起HTTPS請求
    • 直接請求,連接服務器的443端口
  • 服務端的配置
    • 采用HTTPS協議的服務器必須要有一套數字證書,可以自己制作,也可以向組織申請。區別就是自己頒發的證書需要客戶端驗證通過,才可以繼續訪問,而使用受信任的公司申請的證書則不會彈出提示頁面。這套證書其實就是一對公鑰和私鑰。
  • 傳送證書
    • 這個證書其實就是公鑰
  • 客戶端解析證書
    • 這部分工作是有客戶端的TLS/SSL來完成的,首先會驗證公鑰是否有效,比如頒發機構,過期時間等等,如果發現異常,則會彈出一個警告框,提示證書存在問題。如果證書沒有問題,那么就生成一個隨機值。然后用證書對該隨機值進行加密。
  • 傳送加密信息
    • 這部分傳送的是用證書加密后的隨機值,目的就是讓服務端得到這個隨機值,以后客戶端和服務端的通信就可以通過這個隨機值來進行加密解密了。
  • 服務端解密信息
    • 服務端用私鑰解密后,得到了客戶端傳過來的隨機值(私鑰),然后把內容通過該值進行對稱加密。所謂對稱加密就是,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰,不然無法獲取內容。
  • 傳輸加密后的信息
    • 這部分信息是服務端用私鑰加密后的信息,可以在客戶端被還原。
  • 客戶端解密信息
    • 客戶端用之前生成的私鑰解密服務段傳過來的信息,于是獲取了解密后的內容。

這里將流程總結一下:就是用戶發起請求,服務器響應后返回一個證書,證書中包含一些基本信息和公鑰。用戶拿到證書后,去驗證這個證書是否合法,不合法,則請求終止。合法則生成一個隨機數,作為對稱加密的密鑰,用服務器返回的公鑰對這個隨機數加密。然后返回給服務器。服務器拿到加密后的隨機數,利用私鑰解密,然后再用解密后的隨機數(對稱密鑰),把需要返回的數據加密,加密完成后數據傳輸給用戶。最后用戶拿到加密的數據,用一開始的那個隨機數(對稱密鑰),進行數據解密。整個過程完成。

2. 雙向認證

雙向認證,相對于單向認證也很簡單。僅僅多了服務端驗證客戶端這一步。感興趣的可以看看這篇:Https單向認證和雙向認證。


AFSecurityPolicy和認證

1. 認證過程和原理

AFN是靠著AFSecurityPolicy這個類保證數據安全的,調用下面方法用來驗證是否信任服務器。

[self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host])

看一下AFN中的接口

/*!
    @typedef SecTrustRef
    @abstract CFType used for performing X.509 certificate trust evaluations.
    // 執行X.509證書信任評估,其實就是一個容器,裝了服務器端需要驗證的證書的基本信息、
    公鑰等等,不僅如此,它還可以裝一些評估策略,還有客戶端的錨點證書,
    這個客戶端的證書,可以用來和服務端的證書去匹配驗證的
 */
typedef struct CF_BRIDGED_TYPE(id) __SecTrust *SecTrustRef;


/**
 Whether or not the specified server trust should be accepted, based on the security policy.

 This method should be used when responding to an authentication challenge from a server.

 @param serverTrust The X.509 certificate trust of the server.
 @param domain The domain of serverTrust. If `nil`, the domain will not be validated. // 服務器域名

 @return Whether or not to trust the server.
 */
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(nullable NSString *)domain;

根據安全策略是否接受指定的服務器信任。 響應來自服務器的身份驗證質詢時應使用此方法。

大家還記得這個代理方法嗎?

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    //挑戰處理類型為 默認
    /*
     NSURLSessionAuthChallengePerformDefaultHandling:默認方式處理
     NSURLSessionAuthChallengeUseCredential:使用指定的證書
     NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑戰
     */
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    // sessionDidReceiveAuthenticationChallenge是自定義方法,用來如何應對服務器端的認證挑戰

    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
         // 此處服務器要求客戶端的接收認證挑戰方法是NSURLAuthenticationMethodServerTrust
        // 也就是說服務器端需要客戶端返回一個根據認證挑戰的保護空間提供的信任(即challenge.protectionSpace.serverTrust)產生的挑戰證書。
       
        // 而這個證書就需要使用credentialForTrust:來創建一個NSURLCredential對象
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            
            // 基于客戶端的安全策略來決定是否信任該服務器,不信任的話,也就沒必要響應挑戰
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                // 創建挑戰證書(注:挑戰方式為UseCredential和PerformDefaultHandling都需要新建挑戰證書)
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                // 確定挑戰的方式
                if (credential) {
                    //證書挑戰  設計policy,none,則跑到這里
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                //取消挑戰
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            //默認挑戰方式
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
    //完成挑戰
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

這個方法是如何進行接受挑戰的

  • 首先指定了HTTPS為默認的認證方式。
  • 判斷有沒有自定義Block:sessionDidReceiveAuthenticationChallenge,有的話,使用我們自定義Block,生成一個認證方式,并且可以給credential賦值,即我們需要接受認證的證書。然后直接調用completionHandler,去根據這兩個參數,執行系統的認證。
  • 如果沒有自定義Block,我們判斷如果服務端的認證方法要求是NSURLAuthenticationMethodServerTrust,則只需要驗證服務端證書是否安全(即https的單向認證,這是AF默認處理的認證方式,其他的認證方式,只能由我們自定義Block的實現)
  • 接著我們就執行了AFSecurityPolicy相關的上面的方法- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(nullable NSString *)domain,關于這個方法,AF默認的處理是,如果這行返回NO、說明AF內部認證失敗,則取消HTTPS認證,即取消請求。返回YES則進入if塊,用服務器返回的一個serverTrust去生成了一個認證證書。(注:這個serverTrust是服務器傳過來的,里面包含了服務器的證書信息,是用來我們本地客戶端去驗證該證書是否合法用的,后面會更詳細的去講這個參數)然后如果有證書,則用證書認證方式,否則還是用默認的驗證方式。最后調用completionHandler傳遞認證方式和要認證的證書,去做系統根證書驗證。也可以這么理解:這里securityPolicy存在的作用就是,使得在系統底層自己去驗證之前,AF可以先去驗證服務端的證書。如果通不過,則直接越過系統的驗證,取消HTTPS的網絡請求。否則,繼續去走系統根證書的驗證。

2. AFSecurityPolicy實例化

先看一下該類的實例化

AFSecurityPolicy *policy = [AFSecurityPolicy defaultPolicy];

+ (instancetype)defaultPolicy {
    AFSecurityPolicy *securityPolicy = [[self alloc] init];
    securityPolicy.SSLPinningMode = AFSSLPinningModeNone;

    return securityPolicy;
}

這里有一個很重要的屬性就是SSLPinningMode,先看一下這個枚舉

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,    //不驗證
    AFSSLPinningModePublicKey,  //只驗證公鑰
    AFSSLPinningModeCertificate,  // 驗證證書
};

下面我們看一下類AFSecurityPolicy的幾個屬性

/**
 The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`.
 */
// 驗證模式 這個枚舉值上面講述過
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;

/**
 The certificates used to evaluate server trust according to the SSL pinning mode. 

  By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
 
 Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
 */
// 可以去匹配服務端證書驗證的證書
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;

/**
 Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
 */
// 是否支持非法的證書(例如自簽名證書)
@property (nonatomic, assign) BOOL allowInvalidCertificates;

/**
 Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
 */
// 是否去驗證證書域名是否匹配
@property (nonatomic, assign) BOOL validatesDomainName;

后記

本篇主要講述了HTTPS認證原理以及AFSecurityPolicy的實例化。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容