網路程式設計——http協議
1、TCP/IP 基本知識
一、概念
TCP/IP協議是一個協議的集合,它是由眾多的網際網路相關聯的協議集合的總稱。如圖
二、TCP/IP分層管理
TCP/IP模型分為5層:應用層,傳輸層,網路層,資料鏈路層,物理層。(注意:OSI分層是分為七層的)分層的最大好處,就是各層負責各層的任務,一旦某一塊出現問題,則可以直接替換對應的層即可,無需全部更改。
應用層:應用層是我們平時接觸最多的層,它的作用就是向用戶提供應用服務時通訊的活動。比如說http協議,FTP協議,dns(域名解析)等協議,都是在該層
傳輸層:主要作用是提供處於網路連線中的兩臺計算機之間的資料傳輸。如tcp(可靠的傳輸控制協議),udp(使用者資料報協議)。傳輸單位是報文段。
網路層:網路層用來處理網路中流動的資料包,資料包是網路傳輸的最小單位。比如我們常用的IP協議,icmp協議,arp協議(通過IP地址得出對應的mac地址)都在該層。
資料鏈路層:一般用來處理連線硬體部分,如控制網絡卡,硬體相關的裝置驅動。單位是資料幀
物理層:負責資料傳輸的硬體。比如說光纖等等
三、TCP/IP通訊傳輸流
1、tcp/ip協議進行網路通訊時,會通過分層順序和對方進行通訊。傳送端從應用層往下走,接收端則從從下往上走。
2、傳送端在層與層之間傳遞資料時,沒經過一層,就會打上相應層所屬的首部資訊。而在接收端層與層之間傳遞資料時,沒經過一層,則會把對應的首部資訊消去。這種把資料包裝起來的做法,叫做封裝。
四、和http關係密切的協議:IP、TCP、DNS
IP:IP位於網路層,它是一種協議的名稱。注意和IP地址區分。它主要的作用就是把各種資料包傳遞給對方。要保證準確的傳遞到對方手中,其中有兩個重要的條件:1、IP地址 2、MAC地址 ,(IP地址可以和Mac地址進行配對,IP地址可以變換,但MAC地址,一般是不會變的)。
TCP:TCP位於傳輸層,是可靠的傳輸協議(連線需要三次握手,斷開需要四次揮手),它主要是提供可靠的位元組流服務。1、位元組流服務:為了傳輸方便,將大塊的資料分割以報文段為單位的資料包進行管理。 2、可靠的傳輸服務:它能夠把資料準確可靠的傳給對方
DNS服務:該服務是位於應用層的,主要的作用就是解析域名,可以將域名解析出對應的IP地址。
五、各種協議與HTTP協議之間的關係
2、http協議
一、http協議特點
http是位於應用層的一個超文字傳輸協議,基於tcp/ip通訊協議來傳遞資料的。具有以下特點
1、簡單快速:客戶端向服務端請求服務時,只需要傳遞請求方法和路徑。由於http協議簡單,使得http伺服器的程式規模小,因此通訊速度快。
2、靈活:http允許傳輸任意型別的資料物件。正在傳輸的型別由Content-Type標記。
3、無連線:無連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求以後,並且收到客戶端的應答後,就端開連線了。
4、無狀態:http協議是無狀態協議,也就是說對事務處理沒有記憶能力,缺少狀態,如果後續處理需要前面的資訊,則必須要重新傳遞,這樣可能導致每次連線傳輸的資料量增大,但是在伺服器不需要先前的資料的時候,就會快的多了。
5、支援B/S,C/S模式
二、http請求報文和響應報文
請求報文:http請求報文主要有請求行,請求頭,空行,請求體四部分組成。
1、請求行:由請求方法(HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT),URL和協議版本組成。
2、請求頭:為請求報文新增一些資訊,由名/值組成
3、空行:請求頭的最後會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。
4、請求正文
響應報文:http的響應報文由狀態行,響應頭部,空行,響應體組成
1、狀態行:由伺服器HTTP協議版本,響應狀態碼,狀態碼的文字描述組成
2、響應頭部:和請求頭一樣,由名/值組成
3、請求頭的最後會有一個空行,代表請求頭部結束,接下來是請求正文,此部分不可少。
4、響應正文
請求方法
請求頭資訊
響應頭
狀態碼分類
3、https
眾所周知,http是不安全的,它有著很大的缺陷,比如通訊使用明文,不驗證通訊方的身份,無法證明報文的正確性等。所以就有了https。https並不是一個新的協議。https全稱HTTP over TLS。這個TLS位於傳輸層的上層,應用層的下層,作為一個安全層而存在。
注:對於TLS和SSL的區別,大家可以自己去學習。在這裡,只需要理解為TLS是SSL的升級版本就好。
一、https怎麼加密的?
TLS是基於X.509認證的,他假定所有的數字證書都是由一個層次話的數字證書認證機構發出,即CA。他可以通過加密技術(對稱加密和非對稱加密)對我們傳輸的資料進行加密。不瞭解對稱加密和非對稱加密的朋友,可以看看我以前寫的這篇文章:https://www.cnblogs.com/huangjialin/p/9694488.html CA用自己的私鑰簽發數字證書,數字證書中包含有公鑰,然後加密房就可以使用證書中的公鑰解密出CA簽發的證書,從中拿到合法的公鑰。是不是感覺有點懵逼。簡單來說,就是我們一般會在本地內建一個CA證書,然後用本地的CA證書中的公鑰去解密。拿到合法的公鑰,然後就可以用公鑰進行加密資料,然後服務端,就可以使用自己的私鑰就行解密拿到相關的資料。
注意:如果不內建本地證書可以嗎?可以,但是會存在比較大的漏洞,也就是很容易通過中間人攻擊,來截獲資料,所有,內建本地證書並且及時更新證書,這是一種比較有效的防止中間人攻擊的手段。
說了這麼多,我們使用程式碼來解釋一下,拿okhttp來做例子。okhttp新增加密證書的過程。
對okHttp進行簡單的配置,對OKhttp不熟悉的朋友可以看看這篇文章:https://www.cnblogs.com/huangjialin/p/9469373.html
1 OkHttpClient.Builder builder = new OkHttpClient.Builder() 2 .sslSocketFactory() //配置ssl檔案 3 .connectTimeout(15, TimeUnit.SECONDS) 4 .writeTimeout(20, TimeUnit.SECONDS) 5 .readTimeout(20, TimeUnit.SECONDS); 6 OkHttpClient okHttpClient = builder.build(); 7 Request request = new Request.Builder() 8 .get() //設定請求模式 9 .url("https://www.baidu.com/") 10 .build(); 11 12 Call call = okHttpClient.newCall(request); 13 call.enqueue(new Callback() { 14 @Override 15 public void onFailure(Call call, IOException e) { 16 Log.d("MainActivity", "-----------onFailure-----------"); 17 } 18 19 @Override 20 public void onResponse(Call call, Response response) throws IOException { 21 Log.d("MainActivity", "----onResponse----" + response.body().toString()); 22 runOnUiThread(new Runnable() { 23 @Override 24 public void run() { 25 Toast.makeText(MainActivity.this, "請求成功", Toast.LENGTH_LONG).show(); 26 } 27 }); 28 29 } 30 });
okhttp提供了一個方法sslSocketFactory(),專門用來設定ssl的。它需要傳遞一個SSLSocketFactory。
1 private synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 SSLContext sslContext = SSLContext.getInstance("TLS"); 4 sslContext.init(null, null, null); 5 return sslContext.getSocketFactory(); 6 } catch (GeneralSecurityException e) { 7 throw new AssertionError(); 8 } 9 }
上面的使用的是預設的SSLSocketFactory,也就是說什麼也沒有配置,系統提供的。在校驗系統伺服器的時候,會信任裝置內建的100多個證書。那麼他是怎麼校驗的呢?主要是通過TrustManager這個類。在上面程式碼sslContext.init(null, null, null);中,我們都填了null作為引數,但是這樣的話,都沒有辦法進行證書的校驗。我們看看內部原始碼
1 public final void init(KeyManager[] km, TrustManager[] tm, 2 SecureRandom random) 3 throws KeyManagementException { 4 contextSpi.engineInit(km, tm, random); 5 }
1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 SSLContext sslContext = SSLContext.getInstance("TLS"); 4 sslContext.init(null, new TrustManager[]{ 5 new X509TrustManager() { 6 public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { 7 8 } 9 10 public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { 11 } 12 13 public X509Certificate[] getAcceptedIssuers() { 14 return new X509Certificate[0]; 15 } 16 } 17 }, null); 18 return sslContext.getSocketFactory(); 19 } catch (GeneralSecurityException e) { 20 throw new AssertionError(); 21 } 22 }
實際上這樣配置,基本算是完成了。也沒錯,但是使用系統預設提供的SSLSocketFactory,它會預設裝置中內建的100多個證書。基本上是什麼證書都信任了,所以,還是存在很大的風險,中間人很容易就攻擊了。所以,我們還得自己配置自己的SSL證書。
1 private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { 2 try { 3 // 取到證書的輸入流 當然這裡不一定要這樣讀取,就看你的證書存放在哪裡了,huangjialin.crt是證書的名稱 4 InputStream is = new FileInputStream("huangjialin.crt"); 5 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 6 Certificate ca = cf.generateCertificate(is); 7 8 // 建立 Keystore 包含我們的證書 9 String keyStoreType = KeyStore.getDefaultType(); 10 KeyStore keyStore = KeyStore.getInstance(keyStoreType); 11 keyStore.load(null); 12 keyStore.setCertificateEntry(null, ca); 13 14 // 建立一個 TrustManager 僅把 Keystore 中的證書 作為信任的錨點 15 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 16 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); 17 trustManagerFactory.init(keyStore); 18 TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); 19 20 // 用 TrustManager 初始化一個 SSLContext 21 SSLContext sslContext = SSLContext.getInstance("TLS"); 22 sslContext.init(null, trustManagers, null); 23 24 return sslContext.getSocketFactory(); 25 } catch (GeneralSecurityException e) { 26 throw new AssertionError(); 27 } catch (FileNotFoundException e) { 28 e.printStackTrace(); 29 return null; 30 } catch (IOException e) { 31 e.printStackTrace(); 32 return null; 33 } 34 }
通過我們自定義的SSL,自己設定信任錨點,安全性才會大大的提升。
轉載請註明出處:https://www.cnblogs.com/huangjialin/