ASP.NET Web API中的基本驗證
作者:Mike Wasson
基本驗證定義于 RFC 2617、HTTP 驗證:基本和摘要式存取驗證。
缺點
- 系統會在要求中傳送使用者認證。
- 認證會以純文字傳送。
- 認證會隨每個要求一起傳送。
- 無法登出,但結束瀏覽器會話除外。
- 容易受到跨網站偽造要求 (CSRF) ;需要反 CSRF 量值。
優點
- 網際網路標準。
- 所有主要瀏覽器都支援。
- 相對簡單的通訊協定。
基本驗證的運作方式如下:
- 如果要求需要驗證,伺服器會傳回 401 (未經授權) 。 回應包含WWW-Authenticate標頭,表示伺服器支援基本驗證。
- 用戶端會傳送另一個要求,並使用 Authorization 標頭中的用戶端認證。 認證會格式化為字串 「name:password」,base64 編碼。 認證不會加密。
基本驗證是在「領域」的內容內執行。伺服器會在WWW-Authenticate標頭中包含領域的名稱。 使用者的認證在該領域內有效。 領域的實際範圍是由伺服器所定義。 例如,您可以定義數個領域來分割資源。
由於認證會以未加密的方式傳送,因此基本驗證只會透過 HTTPS 保護。 請參閱 在 Web API 中使用 SSL。
基本驗證也很容易遭受 CSRF 攻擊。 在使用者輸入認證之後,瀏覽器會在會話持續期間,自動將後續要求傳送給相同的網域。 這包括 AJAX 要求。 請參閱 防止跨網站偽造要求 (CSRF) 攻擊。
使用 IIS 進行基本驗證
IIS 支援基本驗證,但有一個警告:使用者會根據其 Windows 認證進行驗證。 這表示使用者必須在伺服器的網域上擁有帳戶。 針對公開的網站,您通常會想要針對 ASP.NET 成員資格提供者進行驗證。
若要使用 IIS 啟用基本驗證,請在 ASP.NET 專案的Web.config中,將驗證模式設定為 「Windows」:
<system.web>
<authentication mode="Windows" />
</system.web>
在此模式中,IIS 會使用 Windows 認證進行驗證。 此外,您必須在 IIS 中啟用基本驗證。 在 [IIS 管理員] 中,移至 [功能檢視],選取 [驗證],然後啟用 [基本驗證]。
在您的 Web API 專案中,為任何需要驗證的控制器動作新增 [Authorize]
屬性。
用戶端會在要求中設定 Authorization 標頭來驗證本身。 瀏覽器用戶端會自動執行此步驟。 非瀏覽器用戶端必須設定 標頭。
具有自訂成員資格的基本驗證
如前所述,IIS 內建的基本驗證會使用 Windows 認證。 這表示您必須為主控伺服器上的使用者建立帳戶。 但對於網際網路應用程式,使用者帳戶通常會儲存在外部資料庫中。
下列程式碼如何執行基本驗證的 HTTP 模組。 您可以藉由取代 CheckPassword
方法,輕鬆地插入 ASP.NET 成員資格提供者,這是此範例中的虛擬方法。
在 Web API 2 中,您應該考慮撰寫 驗證篩選器 或 OWIN 中介軟體,而不是 HTTP 模組。
namespace WebHostBasicAuth.Modules
{
public class BasicAuthHttpModule : IHttpModule
{
private const string Realm = "My Realm";
public void Init(HttpApplication context)
{
// Register event handlers
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
// TODO: Here is where you would validate the username and password.
private static bool CheckPassword(string username, string password)
{
return username == "user" && password == "password";
}
private static void AuthenticateUser(string credentials)
{
try
{
var encoding = Encoding.GetEncoding("iso-8859-1");
credentials = encoding.GetString(Convert.FromBase64String(credentials));
int separator = credentials.IndexOf(':');
string name = credentials.Substring(0, separator);
string password = credentials.Substring(separator + 1);
if (CheckPassword(name, password))
{
var identity = new GenericIdentity(name);
SetPrincipal(new GenericPrincipal(identity, null));
}
else
{
// Invalid username or password.
HttpContext.Current.Response.StatusCode = 401;
}
}
catch (FormatException)
{
// Credentials were not formatted correctly.
HttpContext.Current.Response.StatusCode = 401;
}
}
private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader != null)
{
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
// RFC 2617 sec 1.2, "scheme" name is case-insensitive
if (authHeaderVal.Scheme.Equals("basic",
StringComparison.OrdinalIgnoreCase) &&
authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
}
// If the request was unauthorized, add the WWW-Authenticate header
// to the response.
private static void OnApplicationEndRequest(object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
response.Headers.Add("WWW-Authenticate",
string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
若要啟用 HTTP 模組,請將下列內容新增至 system.webServer 區段中的 web.config 檔案:
<system.webServer>
<modules>
<add name="BasicAuthHttpModule"
type="WebHostBasicAuth.Modules.BasicAuthHttpModule, YourAssemblyName"/>
</modules>
將 「YourAssemblyName」 取代為元件名稱, (不包含 「dll」 延伸模組) 。
您應該停用其他驗證配置,例如 Forms 或 Windows 驗證。
意見反應
https://aka.ms/ContentUserFeedback。
即將推出:在 2024 年,我們將隨著內容的意見反應機制逐步淘汰 GitHub 問題,並以新的意見反應系統來取代。 如需詳細資訊,請參閱提交並檢視相關的意見反應