ASP.NET Web API 中的基本身份验证Basic Authentication in ASP.NET Web API

作者: Mike Wassonby Mike Wasson

基本身份验证在RFC 2617 中定义,HTTP 身份验证:基本和摘要式访问身份验证Basic authentication is defined in RFC 2617, HTTP Authentication: Basic and Digest Access Authentication.

缺点Disadvantages

  • 用户凭据是在请求中发送的。User credentials are sent in the request.
  • 凭据以纯文本形式发送。Credentials are sent as plaintext.
  • 凭据随每个请求一起发送。Credentials are sent with every request.
  • 除了在浏览器会话结束以外,没有办法注销。No way to log out, except by ending the browser session.
  • 易受跨站点请求伪造(CSRF)的攻击;需要反 CSRF 度量值。Vulnerable to cross-site request forgery (CSRF); requires anti-CSRF measures.

优点Advantages

  • Internet 标准版。Internet standard.
  • 受所有主要浏览器的支持。Supported by all major browsers.
  • 相对简单的协议。Relatively simple protocol.

基本身份验证的工作原理如下所示:Basic authentication works as follows:

  1. 如果请求要求身份验证,则服务器将返回401(未授权)。If a request requires authentication, the server returns 401 (Unauthorized). 响应包括 WWW 身份验证标头,指示服务器支持基本身份验证。The response includes a WWW-Authenticate header, indicating the server supports Basic authentication.
  2. 客户端向授权标头中的客户端凭据发送另一个请求。The client sends another request, with the client credentials in the Authorization header. 凭据的格式为字符串 "名称: password",采用 base64 编码。The credentials are formatted as the string "name:password", base64-encoded. 凭据未加密。The credentials are not encrypted.

基本身份验证在 "领域" 的上下文中执行。Basic authentication is performed within the context of a "realm." 服务器在 WWW 身份验证标头中包含领域的名称。The server includes the name of the realm in the WWW-Authenticate header. 用户的凭据在该领域内有效。The user's credentials are valid within that realm. 领域的确切范围由服务器定义。The exact scope of a realm is defined by the server. 例如,你可以定义多个领域来对资源进行分区。For example, you might define several realms in order to partition resources.

由于凭据未加密发送,因此基本身份验证仅通过 HTTPS 进行保护。Because the credentials are sent unencrypted, Basic authentication is only secure over HTTPS. 请参阅在 WEB API 中使用 SSLSee Working with SSL in Web API.

基本身份验证也容易受到 CSRF 攻击。Basic authentication is also vulnerable to CSRF attacks. 用户输入凭据后,浏览器会在会话期间自动将其发送到相同域的后续请求。After the user enters credentials, the browser automatically sends them on subsequent requests to the same domain, for the duration of the session. 这包括 AJAX 请求。This includes AJAX requests. 请参阅防止跨站点请求伪造(CSRF)攻击See Preventing Cross-Site Request Forgery (CSRF) Attacks.

带有 IIS 的基本身份验证Basic Authentication with IIS

IIS 支持基本身份验证,但有一些需要注意的事项:根据用户的 Windows 凭据对用户进行身份验证。IIS supports Basic authentication, but there is a caveat: The user is authenticated against their Windows credentials. 这意味着用户必须拥有服务器域中的帐户。That means the user must have an account on the server's domain. 对于面向公众的网站,通常需要针对 ASP.NET 成员资格提供程序进行身份验证。For a public-facing web site, you typically want to authenticate against an ASP.NET membership provider.

若要使用 IIS 启用基本身份验证,请在 ASP.NET 项目的 web.config 中将身份验证模式设置为 "Windows":To enable Basic authentication using IIS, set the authentication mode to "Windows" in the Web.config of your ASP.NET project:

<system.web>
    <authentication mode="Windows" />
</system.web>

在此模式下,IIS 使用 Windows 凭据进行身份验证。In this mode, IIS uses Windows credentials to authenticate. 此外,还必须在 IIS 中启用基本身份验证。In addition, you must enable Basic authentication in IIS. 在 IIS 管理器中转到 "功能" 视图,选择 "身份验证",并启用基本身份验证。In IIS Manager, go to Features View, select Authentication, and enable Basic authentication.

在 Web API 项目中,为需要身份验证的任何控制器操作添加 [Authorize] 特性。In your Web API project, add the [Authorize] attribute for any controller actions that need authentication.

客户端通过设置请求中的 Authorization 标头对自身进行身份验证。A client authenticates itself by setting the Authorization header in the request. 浏览器客户端会自动执行此步骤。Browser clients perform this step automatically. Nonbrowser 客户端将需要设置标头。Nonbrowser clients will need to set the header.

具有自定义成员身份的基本身份验证Basic Authentication with Custom Membership

如前所述,IIS 中内置的基本身份验证使用 Windows 凭据。As mentioned, the Basic Authentication built into IIS uses Windows credentials. 这意味着你需要为你的宿主服务器上的用户创建帐户。That means you need to create accounts for your users on the hosting server. 但对于 internet 应用程序,用户帐户通常存储在外部数据库中。But for an internet application, user accounts are typically stored in an external database.

下面的代码演示如何执行基本身份验证。The following code how an HTTP module that performs Basic Authentication. 您可以通过替换 CheckPassword 方法来轻松插入 ASP.NET 成员资格提供程序,这是本示例中的一个虚方法。You can easily plug in an ASP.NET membership provider by replacing the CheckPassword method, which is a dummy method in this example.

在 Web API 2 中,应考虑编写身份验证筛选器OWIN 中间件,而不是 HTTP 模块。In Web API 2, you should consider writing an authentication filter or OWIN middleware, instead of an HTTP module.

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文件中:To enable the HTTP module, add the following to your web.config file in the system.webServer section:

<system.webServer>
    <modules>
      <add name="BasicAuthHttpModule" 
        type="WebHostBasicAuth.Modules.BasicAuthHttpModule, YourAssemblyName"/>
    </modules>

将 "; Yourassemblyname&gt" 替换为程序集的名称(不包括 "dll" 扩展名)。Replace "YourAssemblyName" with the name of the assembly (not including the "dll" extension).

你应禁用其他身份验证方案,如窗体或 Windows 身份验证。You should disable other authentication schemes, such as Forms or Windows auth.