采用ASP.NET Web API 提供的IAuthenticationFilter和IAuthorizationFilter接口分别实现验证和授权。其中用到IIdentity和IPrincipal接口。
IIdentity的具体类型用来标识通过验证的用户身份,由用户凭据(Credential)来创建一个指定名称的用户。
接口定义:
//定义标识对象的基本功能。
public interface IIdentity
{
// 获取所使用的身份验证的类型。
string AuthenticationType { get; }
// 获取一个值,该值指示是否验证了用户。
bool IsAuthenticated { get; }
// 获取当前用户的名称
string Name { get; }
}
不同的验证类型,对IIdentity有不同的实现方式。如:WindowsIdentity(Windows集成)、FormsIdentity(Forms)和GenericIdentity(一般用户)等。
如果自定义实现验证方式,可以采用GenericIdentity来标识用户身份,已通过验证的用户是一个指定名称的GenericIdentity。
GenericIdentity类型定义:
public class GenericIdentity : ClaimsIdentity
{
// 使用指定的 System.Security.Principal.GenericIdentity 对象初始化 System.Security.Principal.GenericIdentity
// 类的新实例。
protected GenericIdentity(GenericIdentity identity);
// 初始化 System.Security.Principal.GenericIdentity 类的新实例,该类表示具有指定名称的用户。
public GenericIdentity(string name);
// 初始化 System.Security.Principal.GenericIdentity 类的新实例,该类表示具有指定名称和身份验证类型的用户。
public GenericIdentity(string name, string type);
// 获取用于标识用户的身份验证的类型。
public override string AuthenticationType { get; }
// 为用户获取此最常用标识表示的所有声明。
public override IEnumerable<Claim> Claims { get; }
// 获取一个值,该值指示是否验证了用户。
public override bool IsAuthenticated { get; }
// 获取用户的名称。
public override string Name { get; }
// 创建作为当前实例副本的新对象。
public override ClaimsIdentity Clone();
}
IPrincipal的具体类型表示一个已通过验证并获得授权的对象。
接口定义:
public interface IPrincipal
{
// 获取当前用户的标识。
IIdentity Identity { get; }
// 确定当前用户是否属于指定的角色。
bool IsInRole(string role);
}
GenericPrincipal类型,用用户标识和角色名称数组来初始化。
public class GenericPrincipal : ClaimsPrincipal
{
// 从用户标识和角色名称数组(标识表示的用户属于该数组)初始化 System.Security.Principal.GenericPrincipal
// 类的新实例。
public GenericPrincipal(IIdentity identity, string[] roles);
// 获取当前 System.Security.Principal.GenericPrincipal 表示的用户的 System.Security.Principal.GenericIdentity。
public override IIdentity Identity { get; }
// 确定当前 System.Security.Principal.GenericPrincipal 是否属于指定的角色。
public override bool IsInRole(string role);
}
IAuthenticationFilter接口定义:
public interface IAuthenticationFilter : IFilter
{
Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken);
Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken);
}
其中AuthenticateAsync方法用于实现用户认证,而ChallengeAsync方法在认证失败的情况下,生成“质询(Challenge)”结果。
用自定义AuthenticateAttribute类型实现Basic认证:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
public class AuthenticateAttribute : FilterAttribute, IAuthenticationFilter
{
private static readonly Dictionary<string, string> UserAccount;
static AuthenticateAttribute()
{
UserAccount = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{"CHN", "china"}
};
}
public Task AuthenticateAsync(HttpAuthenticationContext context,
System.Threading.CancellationToken cancellationToken)
{
var headerValue = context.Request.Headers.Authorization;
if (null == headerValue || headerValue.Scheme != "Basic")
return Task.Factory.StartNew(() => { }, cancellationToken);
var byteArray = Convert.FromBase64String(headerValue.Parameter);
var credential = Encoding.UTF8.GetString(byteArray);
var split = credential.Split(':');
if (split.Length != 2) return Task.Factory.StartNew(() => { }, cancellationToken);
var userName = split[0];
string password;
if (!UserAccount.TryGetValue(userName, out password))
return Task.Factory.StartNew(() => { }, cancellationToken);
if (password != split[1]) return Task.Factory.StartNew(() => { }, cancellationToken);
var identity=new GenericIdentity(userName);
IPrincipal user = new GenericPrincipal(identity,new string[0]);
context.Principal = user;
return Task.FromResult<object>(null);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
System.Threading.CancellationToken cancellationToken)
{
var user = context.ActionContext.ControllerContext.RequestContext.Principal;
if (null != user && user.Identity.IsAuthenticated)
return Task.Factory.StartNew(() => { }, cancellationToken);
var parameter = string.Format("realm=\"{0}\"", context.Request.RequestUri.DnsSafeHost);
var challenge=new AuthenticationHeaderValue(
"Basic",parameter);
context.Result=new UnauthorizedResult(new[]{challenge},
context.Request);
return Task.FromResult<object>(null);
}
}