ASP.NET Web API Basic Identity 中的基本身份验证
缺点
- 用户凭证在请求中发送。
- 凭据作为明文发送。
- 每个请求都会发送凭据。
- 无法注销,除非结束浏览器会话。
- 易于跨站点请求伪造(CSRF); 需要反CSRF措施。
优点
- 互联网标准。
- 受所有主要浏览器支持。
- 相对简单的协议。
基本认证的工作原理如下:
- 如果请求需要认证,则服务器返回401(未授权)。响应包括WWW-Authenticate头,指示服务器支持基本身份验证。
- 客户端发送另一个请求,其中的客户端凭证位于授权头中。凭据的格式为字符串“name:password”,base64编码。凭证未加密。
基本认证在“领域”的上下文中执行。服务器包括WWW-Authenticate头中的领域名称。用户的凭据在该领域内有效。领域的确切范围由服务器定义。例如,您可以定义多个领域以分区资源。
因为凭据是未加密发送的,所以基本身份验证仅通过HTTPS安全。请参阅在Web API中使用SSL。
基本认证也容易受到CSRF攻击。在用户输入凭据后,浏览器会在会话持续期间自动将它们发送到同一域的后续请求。这包括AJAX请求。请参阅防止跨站点请求伪造(CSRF)攻击。
使用IIS的基本身份验证
IIS支持基本身份验证,但是有一个警告:用户根据其Windows凭据进行身份验证。这意味着用户必须在服务器的域上有一个帐户。对于面向公众的网站,通常需要对ASP.NET成员资格提供程序进行身份验证。1
要使用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模块,添加以下在你的web.config文件system.webServer部分:+
<system.webServer>
<modules>
<add name="BasicAuthHttpModule"
type="WebHostBasicAuth.Modules.BasicAuthHttpModule, YourAssemblyName"/>
</modules>
将“YourAssemblyName”替换为程序集的名称(不包括“dll”扩展名)。
您应该禁用其他身份验证方案,例如Forms或Windows身份验证。