1. 程式人生 > >一個HTTP Basic Authentication引發的異常

一個HTTP Basic Authentication引發的異常

這幾天在做一個功能,其實很簡單。就是呼叫幾個外部的API,返回資料後進行組裝然後成為新的介面。其中一個API是一個很奇葩的API,雖然是基於HTTP的,但既沒有基於SOAP規範,也不是Restful風格的介面。還好使用它也沒有複雜的場景。只是構造出URL,傳送一個HTTP的get請求,然後給我返回一個XML結構的資料。

我使用了Spring MVC中的RestTemplate作為客戶端,然後引入了Jackson-dataformat-xml作為xml對映為物件的工具庫。由於整合外部API的事情已經做了很多次了,整合這個API也是輕車熟路,三下五除二就完成了。

接下來為了驗證連通性,我先在SoapUI裡配置了該外部API的某個測試環境,嘗試傳送了一個Get請求,成功收到了Response。然後我把自己的程式執行起來,嘗試通過自己的程式呼叫該API,結果返回了HTTP 500錯誤,即“internal server error”。

這可奇了怪了。我第一反應是程式中對外部API的配置和SoapUI中的配置不一樣。我仔細對比了傳送請求的URL,需要的HTTP header以及用作驗證的username和password都是完全一致的。這個問題被排除。

接下來我想再仔細看看Response,能否找到什麼蛛絲馬跡。仔細查看了Response的header和body,發現header一切正常,body是個空的body,沒有提供任何的可用資訊。

然後我能想到的另一個解決方案就是聯絡該外部API的團隊,讓他們幫忙看看我傳送了請求之後,為什麼伺服器會返回500。但可惜這是一個很老的服務了,找到該團隊的人並且排期幫我看log至少要花好幾天的時間了。而且既然SoapUI能呼叫成功,而應用程式卻呼叫不成功,問題多半還是出在我們這。

接下來我想既然問題有可能出在我們這,那麼肯定是request有差異。由於我發的是一個Get請求,沒有body實體,URL又完全一樣,那麼問題很可能出在request的header上。這個API需要request中包含兩個自定義的header,而我在SoapUI以及自己的程式中都已經配置了。那問題會在哪裡哪?

既然在SoapUI裡無法重現這個問題,我就使用了Chrome外掛版的POSTMAN,通過它配置了該API的呼叫。然後奇蹟出現了,我竟然在POSTMAN中重現了這個問題。當我看到在POSTMAN也返回了500 error後,我思考了5秒鐘,猜到了原因。問題很可能是出在了Authentication這個header上面。

要說這個問題,還要從HTTP的Basic Authentication說起。Basic Authentication是HTTP實現訪問控制的最簡單的一種技術。HTTP Client端會將使用者名稱和密碼組合後使用Base64加密,生成key為‘Authentication’,value為‘Basic BASE64CODE’的HTTP header,傳送給伺服器端以便進行Basic認證方式。

但這個經典的Basic Authentication是要經歷兩步的。第一步,客戶端傳送不帶Authentication header的HTTP請求,伺服器檢查後發現受訪的資源需要認證,就會返回HTTP Status 401,表示未授權,客戶端發現伺服器端返回401後,會再構造一個新的請求,這次包含了Authentication header,伺服器接收後驗證通過,返回資源。

那麼我在自己的應用程式和POSTMAN中呼叫返回500 internal server error的原因是當第一次給Server傳送不帶Authentication header的HTTP請求時,Server竟然返回了HTTP Status 500。其實它應該返回401,這樣HTTP Client會再發一個包含了Authentication的新請求。由於它返回了500,HTTP Client認為伺服器有問題,就停止處理了。

那為什麼在SoapUI中呼叫可以成功那?那是因為SoapUI使用的Http client在發第一次請求時就已經設定了Authentication header,所以就沒有問題。這樣可以避免重複發請求的現象。這種行為叫做‘preemptive authentication’(搶先驗證),在SoapUI中你可以選擇是否啟用該行為。具體可以參見How To Authenticate SOAP Requests in SoapUI

所以問題的根源在於該外部API在實現Basic Authentication時沒有完全遵循規範,這鍋我們不背

解決方案有兩種。第一種是讓該外部API遵循Basic Authentication的規範,如果請求未授權應該返回401而不是500。不過我說過這是一個很古老的API了,讓它們改要等到猴年馬月了。

第二種就是我的應用程式在給該外部API傳送請求時,第一次就設定Authentication header。我們用的是RestTemplate,而RestTemplate底層使用的是Apache Http Client 4.0+版本。要注入這個header很簡單,在例項化RestTemplate後,給其多加一個Intecepter。

1
2
restTemplate.getInterceptors().add(
  new BasicAuthorizationInterceptor("username", "password"));

加上這一行程式碼後,執行程式,順利的得到了Response,世界清靜了。

最後一個問題,為什麼Http Client當配置了使用者名稱和密碼後,不主動的啟用‘preemptive authentication’那?畢竟可以少發很多請求啊。這是Apache官方給出的原因:

HttpClient does not support preemptive authentication out of the box, because if misused or used incorrectly the preemptive authentication can lead to significant security issues, such as sending user credentials in clear text to an unauthorized third party. Therefore, users are expected to evaluate potential benefits of preemptive authentication versus security risks in the context of their specific application environment. Nonetheless one can configure HttpClient to authenticate preemptively by prepopulating the authentication data cache.

擴充套件閱讀:

相關推薦

一個HTTP Basic Authentication引發異常

這幾天在做一個功能,其實很簡單。就是呼叫幾個外部的API,返回資料後進行組裝然後成為新的介面。其中一個API是一個很奇葩的API,雖然是基於HTTP的,但既沒有基於SOAP規範,也不是Restful風格的介面。還好使用它也沒有複雜的場景。只是構造出URL,傳送一個HTTP的get請求,然後給我返回一個XML

newlisp HTTP Basic Authentication

sta style -m query duration article uil statistic visio HTTP Basic Authentication原來

Web驗證方式--Http Basic Authentication

分享 user omr figure org www gen 一起 host Http Basic Authentication是HTTP協議中定義的Web系統中的驗證方式。參考wiki 主要的實現機制如下: 1. 用戶通過瀏覽器匿名訪問web資源。 2. we

How to enable HTTP Basic Authentication in Spring Security using Java and XML Config

In the last article, I have shown you how to enable Spring security in Java application and today we'll talk about how to enable Basic HTTP authentication

訪問需要HTTP Basic Authentication認證的資源的各種語言的實現

無聊想呼叫下嘀咕的api的時候,發現需要HTTP Basic Authentication,就看了下。 在你訪問一個需要HTTP Basic Authentication的URL的時候,如果你沒有提供使用者名稱和密碼,伺服器就會返回401,如果你直接在瀏覽器中開啟,瀏

C#如何呼叫axis釋出的帶HTTP Basic Authentication驗證的介面配置方式

C#呼叫HTTP BasicAuthentication驗證的介面步驟          由於專案的需要,需C#呼叫帶使用者名稱和密碼的webservice介面。         C#呼叫java的帶使用者名稱和密碼的webservice服務,在網上找了很多資料,也沒有測通

HTTP Basic Authentication認證的資源的C#實現

要在傳送請求的時候新增HTTP Basic Authentication認證資訊到請求中,有兩種方法: 一是在請求頭中新增Authorization: Authorization: "Basic 使用者名稱和密碼的base64加密字串" 二是在url中新增使用者名稱和密碼:

HTTP Basic Authentication認證方式和AFNetworking的header的設定

第一:什麼是 HTTP Basic Authentication?        HTTP Basic Authentication 是一種用來允許Web瀏覽器或其他客戶端程式在請求時提供以使用者名稱

Go實戰--通過basic認證的http(basic authentication)

生命不止, 繼續 go go go !!! 今天就跟大家介紹一下帶有basic認證的api。 何為basic authentication In the context of a HTTP transaction, basic access aut

HttpClient 三種 Http Basic Authentication 認證方式,你瞭解了嗎?

Http Basic 簡介 HTTP 提供一個用於許可權控制和認證的通用框架。最常用的 HTTP 認證方案是 HTTP Basic authentication。Http Basic 認證是一種用來允許網頁瀏覽器或其他客戶端程式在請求時提供使用者名稱和口令形式的身份憑證的一種登入驗證方式。 優點 基本認證的

windows服務呼叫Owin寫一個http API 報錯 呼叫的目標發生了異常

在使用owin開發windows服務的時候,在部署的時候報如下錯誤: 2016-07-02 11:39:48:  讀取配置資訊失敗:呼叫的目標發生了異常。   在 System.RuntimeMethodHandle.InvokeMethod(Object tar

HTTP基本認證(Basic Authentication) 實踐

服務端 nodejs var http = require('http') var server = http.createServer() server.listen(80, function

HTTP基本認證 Basic Authentication 的JAVA示例

                大家在登入網站的時候,大部分時候是通過一個表單提交登入資訊。但是有時候瀏覽器會彈出一個登入驗證的對話方塊,如下圖,這就是使用HTTP基本認證。下面來看看一看這個認證的工作過程:第一步:  客戶端傳送http request 給伺服器,伺服器驗證該使用者是否已經登入驗證過了,如果

格式化程式嘗試對訊息反序列化時引發異常: 嘗試對引數 http://tempuri.org/ 進行反序列化時出錯: GetLzdtArticleResult。InnerException 訊息是“反序

當遇到這個錯誤的時候鬱悶了好長時間報錯是字串長度過大可是修改了MaxStringContentLength”屬性的值卻不起作用最後才發現還是因為配置檔案配置的問題在服務端 格式化程式嘗試對訊息反序列化時引發異常: 嘗試對引數 http://tempuri.org/ 進行反

PHP 模擬 HTTP 基本認證(Basic Authentication

當某個頁面需要認證才能進行訪問時,接到請求後伺服器端會在響應頭中傳送一個 WWW-Authenticate 首部(用來標識認證安全域),語法為 WWW-Authenticate:Basic relam=quoted-string 客戶端接收到後會彈出一個對話方塊,要求

HTTP基本認證(Basic Authentication)的JAVA示例

大家在登入網站的時候,大部分時候是通過一個表單提交登入資訊。但是有時候瀏覽器會彈出一個登入驗證的對話方塊,如下圖,這就是使用HTTP基本認證。下面來看看一看這個認證的工作過程:第一步:  客戶端傳送http request 給伺服器,伺服器驗證該使用者是否已經登入驗證過了,如

HTTP基本認證(Basic Authentication)的java呼叫示例

一、問題引入 大家在登入網站的時候,大部分時候是通過一個表單提交登入資訊。 但是有時候瀏覽器會彈出一個登入驗證的對話方塊,如下圖,這就是使用HTTP基本認證 這種認證的相關介紹在另一篇部落格中

使用裝飾器實現一個簡單的Tornado使用者認證Demo(Basic authentication on Tornado with a decorator)

Tornado is an open source web server developed by Facebook. It implement various third-party authentication schemes to connect to servic

HTTP 驗證 Tomcat中進行基本驗證 (Basic Authentication) 和摘要驗證 (Digest Authentication)

HTTP 驗證HTTP 協議提供驗證機制來保護資源。當一個請求要求取得受保護的資源時,網頁伺服器迴應一個 401 Unauthorized error 錯誤碼。這個迴應包含一個指定了驗證方法和領域的 WWW-Authenticate 頭資訊。把這個領域想像成一個儲存著使用者名稱和密碼的資料庫,它將被用來標識

Spring Security應用開發(04)HTTP basic認證

角色 cati onf poi font con prop ins mode Spring Security默認是使用form-login表單認證方式。 <!-- 默認使用表單認證 --> <sec:form-login /> Spring