第五章 HTTP通信簡介

HTTP的基本概念和基礎

1.1 TCP/IP 協(xié)議族-(HTTP屬于TCP/IP協(xié)議族的一員)

image.png

圖:TCP/IP 是互聯(lián)網(wǎng)相關的各類協(xié)議族的總稱

TCP/IP 協(xié)議族里重要的一點就是分層兰珍。TCP/IP 協(xié)議族按層次分別分
為以下 4 層:應用層、傳輸層兽叮、網(wǎng)絡層和數(shù)據(jù)鏈路層召烂。
應用層
TCP/IP 協(xié)議族內預存了各類通用的應用服務咽弦。比如,F(xiàn)TP(File
Transfer Protocol堡距,文件傳輸協(xié)議)和 DNS(Domain Name System趾断,域
名系統(tǒng))服務就是其中兩類绍申。
HTTP 協(xié)議也處于該層噩咪。
傳輸層
傳輸層對上層應用層,提供處于網(wǎng)絡連接中的兩臺計算機之間的數(shù)據(jù)
傳輸极阅。
在傳輸層有兩個性質不同的協(xié)議:TCP(Transmission Control
Protocol胃碾,傳輸控制協(xié)議)和 UDP(User Data Protocol,用戶數(shù)據(jù)報
協(xié)議)筋搏。
網(wǎng)絡層(又名網(wǎng)絡互連層)
網(wǎng)絡層用來處理在網(wǎng)絡上流動的數(shù)據(jù)包仆百。數(shù)據(jù)包是網(wǎng)絡傳輸?shù)淖钚?shù)
據(jù)單位。該層規(guī)定了通過怎樣的路徑(所謂的傳輸路線)到達對方計
算機奔脐,并把數(shù)據(jù)包傳送給對方俄周。
與對方計算機之間通過多臺計算機或網(wǎng)絡設備進行傳輸時,網(wǎng)絡層所
起的作用就是在眾多的選項內選擇一條傳輸路線髓迎。
鏈路層(又名數(shù)據(jù)鏈路層峦朗,網(wǎng)絡接口層)
用來處理連接網(wǎng)絡的硬件部分。包括控制操作系統(tǒng)排龄、硬件的設備驅
動波势、NIC(Network Interface Card,網(wǎng)絡適配器橄维,即網(wǎng)卡)尺铣,及光纖等
物理可見部分(還包括連接器等一切傳輸媒介)。硬件上的范疇均在
鏈路層的作用范圍之內

1.2 HTTP數(shù)據(jù)的發(fā)送圖解

image.png
image.png

1.3 HTTP和各個協(xié)議的關系

圖解HTTP 彩色版.jpg

二争舞、HTTP通信的基本概念和組成

image.png

人們最初設想的基本理念是:借助多文檔之間相互關聯(lián)形成的超文本
(HyperText)凛忿,連成可相互參閱的 WWW(World Wide Web,萬維
網(wǎng))竞川。

URI和URL
URI 是 Uniform Resource Identifier店溢,用字符串標識某一互聯(lián)網(wǎng)資源,而 URL 表示資源的地點(互聯(lián)網(wǎng)上所處的位置)流译〕言梗可見 URL 是 URI 的子集者疤。

絕對URI的格式

URI的格式.jpg

  1. 協(xié)議方案名福澡,不區(qū)分大小寫,最后附上:結尾驹马。
  2. 登錄信息認證(可選)
  3. 服務器地址革砸,可以采用域名 (比如: hackr.jp)這種DNS可以解析的名稱除秀,或是192.168.1.1以及[0:0:0:0:0:0:0:1]這一類IPV4,IPV6(用方括號括起來,128bit,每一16位為一段)這樣的IP地址名算利。
  4. 服務器網(wǎng)絡的端口號(可選)册踩,如果省略則自動使用默認的端口號。
  5. 帶層級的文件路徑效拭,這個和UNIX謝勇的文件目錄結構類似暂吉。
  6. 查詢字符串(可選),可以使用查詢字符串傳入任意參數(shù)。
  7. 片段標識符(可選)缎患,使用片段標識符可以標記處以獲取資源中的子資源慕的。

基本概念
客戶端(Client):移動應用(iOS、android等應用)
服務器(Server):為客戶端提供服務挤渔、提供數(shù)據(jù)肮街、提供資源的機器
請求(Request):客戶端向服務器索取數(shù)據(jù)的一種行為
響應(Response):服務器對客戶端的請求做出的反應,一般指返回數(shù)據(jù)給客戶端

image.png

URL

URL的基本概念

HTTP協(xié)議的作用
HTTP的全稱是Hypertext Transfer Protocol判导,超文本傳輸協(xié)議
規(guī)定客戶端和服務器之間的數(shù)據(jù)傳輸格式
讓客戶端和服務器能有效地進行數(shù)據(jù)溝通

image.png

HTTP/1.1協(xié)議中嫉父,定義了8種發(fā)送http請求的方法
GET、POST眼刃、OPTIONS绕辖、HEAD、PUT鸟整、DELETE引镊、TRACE、CONNECT篮条、PATCH
根據(jù)HTTP協(xié)議的設計初衷弟头,不同的方法對資源有不同的操作方式
PUT :增
DELETE :刪
POST:改
GET:查
最常用的是GET和POST(實際上GET和POST都能辦到增刪改查)
要想使用GET和POST請求跟服務器進行交互,得先了解一個概念
參數(shù)
就是傳遞給服務器的具體數(shù)據(jù)涉茧,比如登錄時的帳號赴恨、密碼

GET和POST對比
GET和POST的主要區(qū)別表現(xiàn)在數(shù)據(jù)傳遞上
GET
在請求URL后面以?的形式跟上發(fā)給服務器的參數(shù),多個參數(shù)之間用&隔開伴栓,比如
http://ww.test.com/login?username=123&pwd=234&type=JSON
由于瀏覽器和服務器對URL長度有限制伦连,因此在URL后面附帶的參數(shù)是有限制的,通常不能超過1KB

POST
發(fā)給服務器的參數(shù)全部放在請求體中
理論上钳垮,POST傳遞的數(shù)據(jù)量沒有限制(具體還得看服務器的處理能力)

如何選擇Get和Post
如果要傳遞大量數(shù)據(jù)惑淳,比如文件上傳,只能用POST請求
GET的安全性比POST要差些饺窿,如果包含機密\敏感信息歧焦,建議用POST
如果僅僅是索取數(shù)據(jù)(數(shù)據(jù)查詢),建議使用GET
如果是增加肚医、修改绢馍、刪除數(shù)據(jù)向瓷,建議使用POST

HTTP的通信過程 - 請求
請求頭: 包含了對客戶端的環(huán)境描述、客戶端請求信息等
GET /minion.png HTTP/1.1 // 包含了請求方法舰涌、請求資源路徑猖任、HTTP協(xié)議版本
Host: 120.25.226.186:32812 // 客戶端想訪問的服務器主機地址
User-Agent: Mozilla/5.0 // 客戶端的類型,客戶端的軟件環(huán)境
Accept: text/html, / // 客戶端所能接收的數(shù)據(jù)類型
Accept-Language: zh-cn // 客戶端的語言環(huán)境
Accept-Encoding: gzip // 客戶端支持的數(shù)據(jù)壓縮格式

請求體: 客戶端發(fā)給服務器的具體數(shù)據(jù)瓷耙,比如文件數(shù)據(jù)(POST請求才會有)

HTTP通信過程 - 響應
客戶端向服務器發(fā)送請求朱躺,服務器應當做出響應,即返回數(shù)據(jù)給客戶端
HTTP協(xié)議規(guī)定:1個完整的HTTP響應中包含以下內容
響應頭:包含了對服務器的描述搁痛、對返回數(shù)據(jù)的描述
HTTP/1.1 200 OK // 包含了HTTP協(xié)議版本室琢、狀態(tài)碼、狀態(tài)英文名稱
Server: Apache-Coyote/1.1 // 服務器的類型
Content-Type: image/jpeg // 返回數(shù)據(jù)的類型
Content-Length: 56811 // 返回數(shù)據(jù)的長度
Date: Mon, 23 Jun 2014 12:54:52 GMT // 響應的時間

響應體:服務器返回給客戶端的具體數(shù)據(jù)落追,比如文件數(shù)據(jù)
常見的HTTP狀態(tài)碼:
HTTP狀態(tài)碼分類
[圖片上傳中...(image.png-83129d-1522206422531-0)]

狀態(tài)碼 狀態(tài)碼英文名稱 中文描述
100 Continue 繼續(xù)盈滴。客戶端應繼續(xù)其請求
101 Switching Protocols 切換協(xié)議。服務器根據(jù)客戶端的請求切換協(xié)議轿钠。只能切換到更高級的協(xié)議巢钓,例如,切換到HTTP的新版本協(xié)議
200 OK 請求成功疗垛。一般用于GET與POST請求
201 Created 已創(chuàng)建症汹。成功請求并創(chuàng)建了新的資源
202 Accepted 已接受。已經接受請求贷腕,但未處理完成
203 Non-Authoritative Information 非授權信息背镇。請求成功。但返回的meta信息不在原始的服務器泽裳,而是一個副本
204 No Content 無內容瞒斩。服務器成功處理,但未返回內容涮总。在未更新網(wǎng)頁的情況下胸囱,可確保瀏覽器繼續(xù)顯示當前文檔
205 Reset Content 重置內容。服務器處理成功瀑梗,用戶終端(例如:瀏覽器)應重置文檔視圖烹笔。可通過此返回碼清除瀏覽器的表單域
206 Partial Content 部分內容抛丽。服務器成功處理了部分GET請求
300 Multiple Choices 多種選擇谤职。請求的資源可包括多個位置,相應可返回一個資源特征與地址的列表用于用戶終端(例如:瀏覽器)選擇
301 Moved Permanently 永久移動亿鲜。請求的資源已被永久的移動到新URI允蜈,返回信息會包括新的URI,瀏覽器會自動定向到新URI。今后任何新的請求都應使用新的URI代替
302 Found 臨時移動陷寝。與301類似。但資源只是臨時被移動其馏》锱埽客戶端應繼續(xù)使用原有URI
303 See Other 查看其它地址。與301類似叛复。使用GET和POST請求查看
304 Not Modified 未修改仔引。所請求的資源未修改,服務器返回此狀態(tài)碼時褐奥,不會返回任何資源咖耘。客戶端通常會緩存訪問過的資源撬码,通過提供一個頭信息指出客戶端希望只返回在指定日期之后修改的資源
305 Use Proxy 使用代理儿倒。所請求的資源必須通過代理訪問
306 Unused 已經被廢棄的HTTP狀態(tài)碼
307 Temporary Redirect 臨時重定向。與302類似呜笑。使用GET請求重定向
400 Bad Request 客戶端請求的語法錯誤夫否,服務器無法理解
401 Unauthorized 請求要求用戶的身份認證
402 Payment Required 保留朴爬,將來使用
403 Forbidden 服務器理解請求客戶端的請求悍引,但是拒絕執(zhí)行此請求
404 Not Found 服務器無法根據(jù)客戶端的請求找到資源(網(wǎng)頁)。通過此代碼钳榨,網(wǎng)站設計人員可設置"您所請求的資源無法找到"的個性頁面
405 Method Not Allowed 客戶端請求中的方法被禁止
406 Not Acceptable 服務器無法根據(jù)客戶端請求的內容特性完成請求
407 Proxy Authentication Required 請求要求代理的身份認證驼鹅,與401類似微谓,但請求者應當使用代理進行授權
408 Request Time-out 服務器等待客戶端發(fā)送的請求時間過長,超時
409 Conflict 服務器完成客戶端的PUT請求是可能返回此代碼输钩,服務器處理請求時發(fā)生了沖突
410 Gone 客戶端請求的資源已經不存在豺型。410不同于404,如果資源以前有現(xiàn)在被永久刪除了可使用410代碼买乃,網(wǎng)站設計人員可通過301代碼指定資源的新位置
411 Length Required 服務器無法處理客戶端發(fā)送的不帶Content-Length的請求信息
412 Precondition Failed 客戶端請求信息的先決條件錯誤
413 Request Entity Too Large 由于請求的實體過大触创,服務器無法處理,因此拒絕請求为牍。為防止客戶端的連續(xù)請求哼绑,服務器可能會關閉連接。如果只是服務器暫時無法處理碉咆,則會包含一個Retry-After的響應信息
414 Request-URI Too Large 請求的URI過長(URI通常為網(wǎng)址)抖韩,服務器無法處理
415 Unsupported Media Type 服務器無法處理請求附帶的媒體格式
416 Requested range not satisfiable 客戶端請求的范圍無效
417 Expectation Failed 服務器無法滿足Expect的請求頭信息
500 Internal Server Error 服務器內部錯誤,無法完成請求
501 Not Implemented 服務器不支持請求的功能疫铜,無法完成請求
502 Bad Gateway 充當網(wǎng)關或代理的服務器茂浮,從遠端服務器接收到了一個無效的請求
503 Service Unavailable 由于超載或系統(tǒng)維護,服務器暫時的無法處理客戶端的請求。延時的長度可包含在服務器的Retry-After頭信息中
504 Gateway Time-out 充當網(wǎng)關或代理的服務器席揽,未及時從遠端服務器獲取請求
505 HTTP Version not supported 服務器不支持請求的HTTP協(xié)議的版本顽馋,無法完成處理

三、HTTP數(shù)據(jù)安全部分

3.1 數(shù)據(jù)安全的手段

僅僅用POST請求提交用戶的隱私數(shù)據(jù)幌羞,還是不能完全解決安全問題
可以利用軟件(比如Charles)設置代理服務器寸谜,攔截查看手機的請求數(shù)據(jù)
因此:提交用戶的隱私數(shù)據(jù)時,一定不要明文提交属桦,要加密處理后再提交

常見的加密算法
MD5 \ SHA \ DES \ 3DES \ RC2和RC4 \ RSA \ IDEA \ DSA \ AES

加密算法的選擇
一般公司都會有一套自己的加密方案熊痴,按照公司接口文檔的規(guī)定去加密

什么是MD5
全稱是Message Digest Algorithm 5,譯為“消息摘要算法第5版”
效果:對輸入信息生成唯一的128位散列值(32個字符)

MD5的特點
輸入兩個不同的明文不會得到相同的輸出值
根據(jù)輸出值聂宾,不能得到原始的明文果善,即其過程不可逆

MD5的應用
由于MD5加密算法具有較好的安全性,而且免費系谐,因此該加密算法被廣泛使用
主要運用在數(shù)字簽名巾陕、文件完整性驗證以及口令加密等方面
MD5解密網(wǎng)站:http://www.cmd5.com
用戶的隱私數(shù)據(jù),只有在用戶輸入那一刻是明文纪他,其他情況都是密文處理

3.2 HTTPS = HTTP + SSL

由于 HTTP 本身不具備加密的功能惜论,所以也無法做到對通信整體(使
用 HTTP 協(xié)議通信的請求和響應的內容)進行加密。即止喷,HTTP 報文
使用明文(指未經過加密的報文)方式發(fā)送馆类。通信內容在所有的通信線路上都有可能遭到窺視。


竊聽.jpg

HTTP 協(xié)議中沒有加密機制弹谁,但可以通過和 SSL(Secure Socket Layer乾巧,安全套接層)或TLS(Transport Layer Security,安全層傳輸協(xié)議)的組合使用预愤,加密 HTTP 的通信內容沟于。用 SSL 建立安全通信線路之后,就可以在這條線路上進行 HTTP通信了植康。與 SSL 組合使用的 HTTP 被稱為 HTTPS(HTTPSecure旷太,超文本傳輸安全協(xié)議)或 HTTP over SSL。

3.2.1 HTTPS 是身披 SSL 外殼的 HTTP

通常销睁,HTTP 直接和 TCP 通信供璧。當使用 SSL 時,則演變成先和 SSL 通信冻记,再由 SSL 和 TCP 通信了睡毒。簡言之,所謂 HTTPS冗栗,其實就是身披SSL 協(xié)議這層外殼的 HTTP演顾。


HTTPS.jpg

3.2.2 具體的通信步驟

https具體通信步驟.jpg
  • 步驟 1: 客戶端通過發(fā)送 Client Hello 報文開始 SSL 通信供搀。報文中包含客戶端支持的 SSL 的指定版本、加密組件(Cipher Suite)列表(所使用的加密算法及密鑰長度等)钠至。
  • 步驟 2: 服務器可進行 SSL 通信時葛虐,會以 Server Hello 報文作為應答。和客戶端一樣棉钧,在報文中包含 SSL 版本以及加密組件屿脐。服務器的加密組件內容是從接收到的客戶端加密組件內篩選出來的
  • 步驟 3: 之后服務器發(fā)送 Certificate 報文掰盘。報文中包含公開密鑰證書
  • 步驟 4: 最后服務器發(fā)送 Server Hello Done 報文通知客戶端赞季,最初階段的 SSL 握手協(xié)商部分結束愧捕。
  • 步驟 5: SSL 第一次握手結束之后,客戶端以 Client Key Exchange 報文作為回應申钩。報文中包含通信加密中使用的 一種被稱為Pre-mastersecret 的隨機密碼串次绘。該報文已用步驟 3 中的公開密鑰進行加密
    步驟 6: 接著客戶端繼續(xù)發(fā)送 Change Cipher Spec 報文撒遣。該報文會提示服務器邮偎,在此報文之后的通信會采用 Pre-master secret 密鑰加密
    步驟 7: 客戶端發(fā)送 Finished 報文义黎。該報文包含連接至今全部報文的整體校驗值禾进。這次握手協(xié)商是否能夠成功,要以服務器是否能夠正確解密該報文作為判定標準廉涕。
    步驟 8: 服務器同樣發(fā)送 Change Cipher Spec 報文泻云。
    步驟 9: 服務器同樣發(fā)送 Finished 報文。
    步驟 10: 服務器和客戶端的 Finished 報文交換完畢之后狐蜕,SSL 連接就算建立完成宠纯。當然,通信會受到 SSL 的保護层释。從此處開始進行應用層協(xié)議的通信婆瓜,即發(fā)送 HTTP 請求。
    步驟 11: 應用層協(xié)議通信贡羔,即發(fā)送 HTTP 響應廉白。
    步驟 12: 最后由客戶端斷開連接。斷開連接時乖寒,發(fā)送close_notify 報文蒙秒。上圖做了一些省略,這步之后再發(fā)送 TCP FIN 報文來關閉與 TCP的通信宵统。

3 3.2.3 關于證書

HTTPS通信圖解2

https步驟詳解.jpg

為方便大家理解這個這個圖晕讲,拓展一步
image.png

數(shù)字證書認證機構處于客戶端與服務器雙方都可信賴的第三方機構的
立場上覆获。 威瑞信( VeriSign) 就是其中一家非常有名的數(shù)字證書認證
機構。 我們來介紹一下數(shù)字證書認證機構的業(yè)務流程瓢省。

  1. 服務器的運營人員向數(shù)字證書認證機構提出公開密鑰的申請弄息。 數(shù)字證書認證機構在判明提出申請者的身份之后, 會對已申請的公開密鑰做數(shù)字簽名勤婚, 然后分配這個已簽名的公開密鑰摹量, 并將該公開密鑰放入公鑰證書后綁定在一起。
  2. 服務器會將這份由數(shù)字證書認證機構頒發(fā)的公鑰證書發(fā)送給客戶端馒胆,以進行公開密鑰加密方式通信缨称。 公鑰證書也可叫做數(shù)字證書或直接稱為證書
  3. 接到證書的客戶端可使用數(shù)字證書認證機構的公開密鑰祝迂, 對那張證書上的數(shù)字簽名進行驗證睦尽, 一旦驗證通過, 客戶端便可明確兩件事:一型雳, 認證服務器的公開密鑰的是真實有效的數(shù)字證書認證機構当凡。 二,服務器的公開密鑰是值得信賴的纠俭。
    此處認證機關的公開密鑰必須安全地轉交給客戶端沿量。 使用通信方式時, 如何安全轉交是一件很困難的事冤荆, 因此朴则, 多數(shù)瀏覽器開發(fā)商發(fā)布版本時, 會事先在內部植入常用認證機構的公開密鑰钓简,其中MAC電腦和iphone都是由系統(tǒng)的鑰匙串工具進行統(tǒng)一管理佛掖。
3.2.3.1 可證明組織真實性的EV SSL證書

證書的作用是:1.判斷通信一方的服務器是否規(guī)范 2.證明服務器背后的運營的企業(yè)是否真實存在。
所以只有具備以上特性的證書才是EV SSL(Extended Validation SSL Certificate)證書涌庭。
典型持有EV SSL證書的web網(wǎng)站將如下圖,綠色加鎖芥被。此舉主要是為了防止釣魚網(wǎng)站。


image.png
3.2.3.2 用于確認客戶端的證書

當然HTTPS還可以有客戶端證書坐榆。以客戶端證書來對客戶端進行認證拴魄。以便于服務器確認正在通信的客戶端是預料中的客戶端。
但是客戶端證書存在著一下問題點:(市面上只有網(wǎng)銀會采用客戶端證書席镀,U盾實質是 用于網(wǎng)上銀行電子簽名及數(shù)字認證的工具匹中,其通過獨特的技術對網(wǎng)上數(shù)據(jù)進行加 密、解密及數(shù)字簽名豪诲,以保證網(wǎng)上交易的保密性顶捷、真實性、完整性和不可否認性屎篱。)

  1. 想獲取證書是服赎,用戶得自行安裝客戶端證書葵蒂。而客戶端證書需要付費購買,且每張證書對應每位用戶也就是說需要支付和用戶數(shù)對等的費用重虑。代價昂貴践付。
  2. 讓知識層面不同的用戶們自行安裝證書,這件事情也充滿各種挑戰(zhàn)缺厉。
3.2.3.2 認證機構

SSL機制中介入認證機構之所以可行永高, 是因為建立在其信用絕對可靠這一大前提下的。 然而提针, 2011 年 7 月命爬, 荷蘭的一家名叫DigiNotar 的認證機構曾遭黑客不法入侵, 頒布了 google.com 和twitter.com 等網(wǎng)站的偽造證書事件辐脖。 這一事件從根本上撼動了SSL的可信度饲宛。
因為偽造證書上有正規(guī)認證機構的數(shù)字簽名, 所以瀏覽器會判定
該證書是正當?shù)摹?當偽造的證書被用做服務器偽裝之時揖曾, 用戶根
本無法察覺到

3.2.3.3 子認證機構頒發(fā)的證書稱為自簽名證書

如果使用 OpenSSL這套開源程序落萎, 每個人都可以構建一套屬于自己的認證機構亥啦, 從而自己給自己頒發(fā)服務器證書炭剪。 但該服務器證書在互網(wǎng)上不可作為證書使用, 似乎沒什么幫助翔脱。獨立構建的認證機構叫做認證機構奴拦, 由自認證機構頒發(fā)的“無用”證書也被戲稱為自簽名證書。瀏覽器訪問該服務器時届吁, 會顯示“無法確認連接安全性”或“該網(wǎng)站的安全證書存在問題”等警告信息错妖。

3.2.3.4 客戶端驗證過程簡析

所以從上圖可以看出,服務器公開密鑰的證書包含公開密鑰和CA簽名疚沐,首先客戶端通過系統(tǒng)或者瀏覽器中存在CA根證書暂氯,來驗證該證書的可靠性。然后通過系統(tǒng)或者瀏覽器中存在該CA的公開密鑰進行驗證對應的CA簽名亮蛔,驗證了證書的有效性之后痴施,取出服務器的公開密鑰。

3.2.3.5 證書的內容

X.509 應該是比較流行的 SSL 數(shù)字證書標準究流,包含(但不限于)以下的字段:


image.png

比如京東的證書:


image.png

image.png

image.png
3.2.3.6 數(shù)字證書的生成及驗證

數(shù)字證書的生成是分層級的辣吃,下一級的證書需要其上一級證書的私鑰簽名。
所以后者是前者的證書頒發(fā)者芬探,也就是說上一級證書的 Subject Name 是其下一級證書的 Issuer Name神得。
在得到證書申請者的一些必要信息(對象名稱,公鑰私鑰)之后偷仿,證書頒發(fā)者通過 SHA-256 哈希得到證書內容的摘要哩簿,再用自己的私鑰給這份摘要加密宵蕉,得到數(shù)字簽名。綜合已有的信息卡骂,生成分別包含公鑰和私鑰的兩個證書国裳。
扯到這里,就有幾個問題:

問:如果說發(fā)布一個數(shù)字證書必須要有上一級證書的私鑰加密全跨,那么最頂端的證書——根證書怎么來的缝左?

根證書是自簽名的,即用自己的私鑰簽名浓若,不需要其他證書的私鑰來生成簽名渺杉。

問:怎么驗證證書是有沒被篡改?

當客戶端走 HTTPS 訪問站點時挪钓,服務器會返回整個證書鏈是越。以下圖的證書鏈為例:

要驗證 *.wikipedia.org 這個證書有沒被篡改,就要用到 GlobalSign Organization Validation CA - SHA256 - G2 提供的公鑰解密前者的簽名得到摘要 Digest1碌上,我們的客戶端也計算前者證書的內容得到摘要 Digest2倚评。對比這兩個摘要就能知道前者是否被篡改。后者同理馏予,使用 GlobalSign Root CA 提供的公鑰驗證天梧。當驗證到到受信任的根證書時,就能確定 *.wikipedia.org 這個證書是可信的霞丧。

問:為什么上面那個根證書 GlobalSign Root CA受信任的呢岗?

數(shù)字證書認證機構(Certificate Authority, CA)簽署和管理的 CA 根證書,會被納入到你的瀏覽器和操作系統(tǒng)的可信證書列表中蛹尝,并由這個列表判斷根證書是否可信后豫。所以不要隨便導入奇奇怪怪的根證書到你的操作系統(tǒng)中。

問:生成的數(shù)字證書(如 *.wikipedia.org)都可用來簽署新的證書嗎突那?

不一定挫酿。如下圖,拓展字段里面有個叫 Basic Constraints 的數(shù)據(jù)結構愕难,里面有個字段叫路徑長度約束(Path Length Constraint)早龟,表明了該證書能繼續(xù)簽署 CA 子證書的深度,這里為0务漩,說明這個 GlobalSign Organization Validation CA - SHA256 - G2 只能簽署客戶端證書拄衰,而客戶端證書不能用于簽署新的證書,CA 子證書才能這么做饵骨。

3.2.3.7 iOS 上對證書鏈的驗證

Overriding TLS Chain Validation Correctly 中提到:

When a TLS certificate is verified, the operating system verifies its chain of trust. If that chain of trust contains only valid certificates and ends at a known (trusted) anchor certificate, then the certificate is considered valid.

所以在 iOS 中翘悉,證書是否有效的標準是:

信任鏈中如果只含有有效證書并且以可信錨點(trusted anchor)結尾,那么這個證書就被認為是有效的居触。

其中可信錨點指的是系統(tǒng)隱式信任的證書妖混,通常是包括在系統(tǒng)中的 CA 根證書老赤。不過你也可以在驗證證書鏈時,設置自定義的證書作為可信的錨點制市。

3.2.4 TCP層面的具體通信流程

timg.jpeg

四抬旺、iOS層面的SSL層面的實現(xiàn)

4.1 NSURLSession 實現(xiàn) HTTPS

具體到使用 NSURLSession 走 HTTPS 訪問網(wǎng)站,-URLSession:didReceiveChallenge:completionHandler: 回調中會收到一個 challenge祥楣,也就是質詢开财,需要你提供認證信息才能完成連接。這時候可以通過 challenge.protectionSpace.authenticationMethod 取得保護空間要求我們認證的方式误褪,如果這個值是 NSURLAuthenticationMethodServerTrust 的話责鳍,我們就可以插手 TLS 握手中“驗證數(shù)字證書有效性”這一步。

4.2 默認的實現(xiàn)

系統(tǒng)的默認實現(xiàn)(也即代理不實現(xiàn)這個方法)是驗證這個信任鏈兽间,結果是有效的話則根據(jù) serverTrust 創(chuàng)建 credential 用于同服務端確立 SSL 連接历葛。否則會得到 “The certificate for this server is invalid…” 這樣的錯誤而無法訪問。

4.3 自定義實現(xiàn)

4.3.1 -(void)URLSession:(NSURLSession *)session

    didReceiveChallenge 代理說明

如果我們要實現(xiàn)這個代理方法的話嘀略,需要提供 NSURLSessionAuthChallengeDisposition(處置方式)和 NSURLCredential(資格認證)這兩個參數(shù)給 completionHandler 這個 block:

-(void)URLSession:(NSURLSession *)session 
        didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
        completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, 
                    NSURLCredential * _Nullable))completionHandler {

    // 如果使用默認的處置方式恤溶,那么 credential 就會被忽略
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;

    if ([challenge.protectionSpace.authenticationMethod
            isEqualToString: 
            NSURLAuthenticationMethodServerTrust]) {

        /* 調用自定義的驗證過程 */
        if ([self myCustomValidation:challenge]) {    
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            if (credential) {
                disposition = NSURLSessionAuthChallengeUseCredential;
            }    
        } else {
            /* 無效的話,取消 */
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge
        }
    }        
    if (completionHandler) {
        completionHandler(disposition, credential);
    } 
}

在 [self myCustomValidation:challenge] 調用自定義驗證過程帜羊,結果是有效的話才創(chuàng)建 credential 確立連接咒程。
自定義的驗證過程,需要先拿出一個 SecTrustRef 對象逮壁,它是一種執(zhí)行信任鏈驗證的抽象實體孵坚,包含著驗證策略(SecPolicyRef)以及一系列受信任的錨點證書粮宛,而我們能做的也是修改這兩樣東西而已窥淆。

SecTrustRef trust = challenge.protectionSpace.serverTrust;

拿到 trust 對象之后,可以用下面這個函數(shù)對它進行驗證巍杈。

static BOOL serverTrustIsVaild(SecTrustRef trust) {
    BOOL allowConnection = NO;

// 假設驗證結果是無效的
SecTrustResultType trustResult = kSecTrustResultInvalid;

// 函數(shù)的內部遞歸地從葉節(jié)點證書到根證書的驗證
OSStatus statue = SecTrustEvaluate(trust, &trustResult);

    if (statue == noErr) {
    // kSecTrustResultUnspecified: 系統(tǒng)隱式地信任這個證書
    // kSecTrustResultProceed: 用戶加入自己的信任錨點忧饭,顯式地告訴系統(tǒng)這個證書是值得信任的

    allowConnection = (trustResult == kSecTrustResultProceed 
                                || trustResult == kSecTrustResultUnspecified);
    }
    return allowConnection;
}

4.2.2 域名驗證

可以通過以下的代碼獲得當前的驗證策略:

CFArrayRef policiesRef;
SecTrustCopyPolicies(trust, &policiesRef);

打印 policiesRef 后,你會發(fā)現(xiàn)默認的驗證策略就包含了域名驗證筷畦,即“服務器證書上的域名和請求域名是否匹配”词裤。如果你的一個證書需要用來連接不同域名的主機,或者你直接用 IP 地址去連接鳖宾,那么你可以重設驗證策略以忽略域名驗證:

NSMutableArray *policies = [NSMutableArray array];
// BasicX509 不驗證域名是否相同
SecPolicyRef policy = SecPolicyCreateBasicX509();
[policies addObject:(__bridge_transfer id)policy];

然后再調用 serverTrustIsVaild() 驗證吼砂。
但是如果不驗證域名的話,安全性就會大打折扣

4.2.3 自簽名的證書鏈驗證

在 App 中想要防止中間人攻擊鼎文,比較好的做法是將公鑰證書打包進 App 中渔肩,然后在收到服務端證書鏈的時候,能夠有效地驗證服務端是否可信拇惋,這也是驗證自簽名的證書鏈所必須做的周偎。

假設你的服務器返回:[你的自簽名的根證書] — [你的二級證書] — [你的客戶端證書]抹剩,系統(tǒng)是不信任這個三個證書的。
所以你在驗證的時候需要將這三個的其中一個設置為錨點證書蓉坎,當然澳眷,多個也行。

比如將 [你的二級證書] 作為錨點后蛉艾,SecTrustEvaluate() 函數(shù)只要驗證到 [你的客戶端證書] 確實是由 [你的二級證書] 簽署的钳踊,那么驗證結果為 kSecTrustResultUnspecified,表明了 [你的客戶端證書] 是可信的勿侯。下面是設置錨點證書的做法:

NSMutableArray *certificates = [NSMutableArray array];
 
NSDate *cerData = /* 在 App Bundle 中你用來做錨點的證書數(shù)據(jù)箍土,證書是 CER 編碼的,常見擴展名有:cer, crt...*/
 
SecCertificateRef cerRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData);
 
[certificates addObject:(__bridge_transfer id)cerRef];
 
// 設置錨點證書罐监。
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certificates);

只調用 SecTrustSetAnchorCertificates () 這個函數(shù)的話吴藻,那么就只有作為參數(shù)被傳入的證書作為錨點證書,連系統(tǒng)本身信任的 CA 證書不能作為錨點驗證證書鏈弓柱。要想恢復系統(tǒng)中 CA 證書作為錨點的功能沟堡,還要再調用下面這個函數(shù):

// true 代表僅被傳入的證書作為錨點,false 允許系統(tǒng) CA 證書也作為錨點
SecTrustSetAnchorCertificatesOnly(trust, false);

這樣矢空,再調用 serverTrustIsVaild() 驗證證書有效性就能成功了航罗。

4.2.4 APP里面打包證書

對于一些校驗更加嚴格的應用,有時需要更加嚴格的https校驗屁药。因此服務器會事先把證書給到客戶端粥血,客戶端在進行https通信或者更應該說是ssl通信的時候(tcp)層面時候,需要操作如下:

  • step1: 從服務器工程師中獲取我們公司服務器下發(fā)給對應項目的證書酿箭,如下圖我們需要使用的二級證書(有些可能是三級證書)复亏。

    670BBEDF-7B62-4DA3-A1C3-43A50CA4812A.png
  • step2:將該證書生成.cer的格式,IOS端或者AFNetworking只支持cer的格式
    方法很多種缭嫡,比如向命令行中輸入(MAC電腦上鑰匙串工具也不失為一個好的選擇):

openssl x509 -in 你的證書.crt -out 你的證書.cer -outform der

  • step3:將證書拖入工程中,注意一定要勾選“Add to targets”這樣在 [NSBundle mainBundle]才能找得到證書缔御。

    47157233-5C8C-4E33-AB18-294C3E765255.png
  • step4: 根據(jù)客戶端證書來驗證服務器證書的有效性

4.2.4.1 TCP層面的SSL驗證(這里基于GCDAsyncSocket為例)
如果需要進行SSL通信,那么再代理里面妇蛀,調用startTLS: 函數(shù)
-(void) socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
    if (useSSL) {
        if (self.sslHandlerBlock) {
            NSDictionary* dict = self.sslHandlerBlock();
            [_tcpSocket startTLS:dict];
        }else{
            [_tcpSocket startTLS:nil];
        }
    }else{
#if ReadFrame
        //開始使能接收
        [_tcpSocket readDataToLength:HeadNoHiddenLen withTimeout:-1 tag:HeadNoHiddenTag];
#else
        [_tcpSocket readDataWithTimeout:-1 tag:-1];
#endif
    }
    if ([self.remoteServiceDelegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) {
        [self.remoteServiceDelegate socket:sock didConnectToHost:host port:port];
    }
}

其中dictionary里面的內容為:

(lldb) po dict
{
GCDAsyncSocketManuallyEvaluateTrust = 1;
kCFStreamSSLPeerName = "bolai-test.yunext.com";
}
具體的可以查看startTLS: 函數(shù)的說明耕突,其中GCDAsyncSocketManuallyEvaluateTrust 為YES是為了其能調用以下代理方法,告知我們需要自己參與證書校驗评架。
kCFStreamSSLPeerName: 服務于域名校驗

- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust: 里面的函數(shù)處理

其中線程方面特別注意眷茁,注意閱讀該函數(shù)的使用,在此不再贅述纵诞。

- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust
completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
{
    /**************************************************************
     該方法中需要特別注意上祈,對于證書的驗證有一個方法會阻塞線程,所以這里需要單獨處理
     **************************************************************/
    NSOperationQueue* queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        BOOL isValid = NO;
        isValid = [SSLTool evaluateServerTrust:trust];
        completionHandler(isValid);
    }];
}

+ (BOOL)evaluateServerTrust:(SecTrustRef)trust
{
    NSMutableArray *policies = [NSMutableArray array];
    //需要校驗域名 AppBalanceHost 是一個定義好的宏
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)AppBalanceHost)];
    SecTrustSetPolicies(trust, (__bridge CFArrayRef)policies);
    
    NSMutableArray *pinnedCertificates = [NSMutableArray array];
    //[self defaultPinnedCertificates] bundle里面的cer證書的 數(shù)據(jù)
    for (NSData *certificateData in [self defaultPinnedCertificates]) {
        [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
    }
    //設置證書錨點
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)pinnedCertificates);
    // 驗證該證書是否有效
    if (!SSLServerTrustIsValid(trust)) {
        return NO;
    }
    // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
    // 將服務器返回的證書鏈,轉化為證書列表
    NSArray *serverCertificates = SSLCertificateTrustChainForServerTrust(trust);
    //判斷服務器返回證書是否在本地的 bundle列表(即APP打包進來客戶端可以相信的證書列表)
    for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
        if ([[self defaultPinnedCertificates] containsObject:trustChainCertificate]) {
            return YES;
        }
    }
    return NO;
}

static BOOL SSLServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
    
    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
    
_out:
    return isValid;
}

static NSArray * SSLCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
    }
    
    return [NSArray arrayWithArray:trustChain];
}

4.2.5 HTTPS通信的實現(xiàn)

自定義實現(xiàn)證書授信雇逞,前文已經進行了詳盡的描述荤懂,這里講解下AFNetworking的實現(xiàn)

+ (void)inithttps:(AFHTTPSessionManager *)mgr{
// allowInvalidCertificates      是否允許無效證書(也就是自建的證書),默認為NO(如果采用自簽名證書請改為YES)
//validatesDomainName       是否進行域名校驗塘砸,建議都為YES
//pinnedCertificates              如果需要采用APP打包近來的證書校驗节仿,請將.cer的數(shù)據(jù)包裝成NSMutableSet并且賦值
// policyWithPinningMode: 方法中3個參數(shù)的含義見下面
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    securityPolicy.allowInvalidCertificates = NO;
    securityPolicy.validatesDomainName = YES;
//    securityPolicy.pinnedCertificates = cerSet;
    mgr.securityPolicy = securityPolicy;
}

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,
    AFSSLPinningModeCertificate,
};

AFSSLPinningModeNone:
這個模式表示不做SSL pinning,只跟瀏覽器一樣在系統(tǒng)的信任機構列表里驗證服務端返回的證書掉蔬。若證書是信任機構簽發(fā)的就會通過廊宪,若是自己服務器生成的證書就不會通過。
AFSSLPinningModePublicKey:
代表客戶端會將服務器端返回的證書與本地保存的證書中女轿,PublicKey的部分進行校驗箭启;如果正確,才繼續(xù)進行蛉迹。
AFSSLPinningModeCertificate: 代表客戶端會將服務器端返回的證書和本地保存的證書中的所有內容傅寡,包括PublicKey和證書部分,全部進行校驗北救;如果正確荐操,才繼續(xù)進行。

4.2.6 常見證書調試觀察服務器的返回證書結果

服務器推送過來的證書珍策,包含著兩級托启,一級為GlobalSign Domain Validation CA - SHA256 - G 其父證書為 GlobalSign Root CA
二級為通配域名證書(^.yunext.com i)的證書,該證書是父節(jié)點證書GlobalSign Domain Validation CA - SHA256 - G

image.png

如果是自簽名的證書調試結果如下:
該證書的父節(jié)點證書還是自身攘宙,沒有可信的父節(jié)點證書屯耸。

image.png

該結果的result的返回 kSecTrustResultFatalTrustFailure

采用 Let’s Encrypt 進行免費簽名https證書(典型的中級認證家機構,有可能會被當成自認證證書蹭劈,當然從其官網(wǎng)可以看出目前和他的合作機構是比較多的)

image.png
image.png

參考書籍

《計算機網(wǎng)絡自頂向下方法》
《圖解HTTP》 -- 上野 宣【日】
《圖解TCP疗绣、IP》

參考鏈接

GCDAsyncSocket two way authentication SSL 雙向認證
//典型的應用的blog
https://www.cnblogs.com/whoislcj/p/6369717.html
iOS 中對 HTTPS 證書鏈的驗證【推薦】
GCDAsyncSocket的使用說明鏈接
https://github.com/robbiehanson/CocoaAsyncSocket/wiki/Reference_GCDAsyncSocket
AFNetworking的使用說明鏈接
http://cocoadocs.org/docsets/AFNetworking/3.1.0/
從SSL安全傳輸?shù)絠OS證書安全體系1
從SSL安全傳輸?shù)絠OS證書安全體系2
SSL工作原理
AFNetworking解析(四)
AFNetworking 概述(一)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市链方,隨后出現(xiàn)的幾起案子持痰,更是在濱河造成了極大的恐慌灶搜,老刑警劉巖祟蚀,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異割卖,居然都是意外死亡前酿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門鹏溯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罢维,“玉大人,你說我怎么就攤上這事丙挽》畏酰” “怎么了匀借?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長平窘。 經常有香客問我吓肋,道長冷冗,這世上最難降的妖魔是什么返帕? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任亏推,我火速辦了婚禮煤墙,結果婚禮上熄守,老公的妹妹穿的比我還像新娘幢码。我一直安慰自己但绕,他們只是感情好示绊,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布芒率。 她就那樣靜靜地躺著囤耳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪偶芍。 梳的紋絲不亂的頭發(fā)上紫皇,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音腋寨,去河邊找鬼聪铺。 笑死,一個胖子當著我的面吹牛萄窜,可吹牛的內容都是我干的铃剔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼查刻,長吁一口氣:“原來是場噩夢啊……” “哼键兜!你這毒婦竟也來了?” 一聲冷哼從身側響起穗泵,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤普气,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后佃延,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體现诀,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年履肃,在試婚紗的時候發(fā)現(xiàn)自己被綠了仔沿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡尺棋,死狀恐怖封锉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤成福,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布碾局,位于F島的核電站,受9級特大地震影響奴艾,放射性物質發(fā)生泄漏擦俐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一握侧、第九天 我趴在偏房一處隱蔽的房頂上張望蚯瞧。 院中可真熱鬧,春花似錦品擎、人聲如沸埋合。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甚颂。三九已至,卻和暖如春秀菱,著一層夾襖步出監(jiān)牢的瞬間振诬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工衍菱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赶么,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓脊串,卻偏偏與公主長得像辫呻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子琼锋,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • 1.OkHttp源碼解析(一):OKHttp初階2 OkHttp源碼解析(二):OkHttp連接的"前戲"——HT...
    隔壁老李頭閱讀 20,811評論 24 176
  • 前面兩篇文章中關于 HTTP 相關知識基本上介紹的差不多了放闺,這篇文章是對 HTTP 協(xié)議的補充,主要介紹以下三點內...
    lijiankun24閱讀 1,301評論 2 3
  • 1. 網(wǎng)絡基礎TCP/IP HTTP基于TCP/IP協(xié)議族缕坎,HTTP屬于它內部的一個子集怖侦。 把互聯(lián)網(wǎng)相關聯(lián)的協(xié)議集...
    yozosann閱讀 3,437評論 0 20
  • 一、作用 不使用SSL/TLS的HTTP通信谜叹,就是不加密的通信匾寝。所有信息明文傳播,帶來了三大風險叉谜。 (1)竊聽風險...
    XLsn0w閱讀 10,481評論 2 44
  • 早上去吃早飯旗吁,碰到海清教練,他給我說了很多思維層面的東西停局,很開闊眼界。也的確讓自己的目標更清晰,那就是總教練董栽,無論...
    Hi_張閱讀 135評論 0 0