【ASP.NET Core 认证】AuthenticationOptions、AuthenticationSchemeOptions、AuthenticateResult、IClaimsTransformation

AuthenticationOptions(整个应用的身份验证选项)

AuthenticationSchemeOptions是指某个具体身份验证方案的选项。AuthenticationOptions则是针对整个身份验证功能的选项对象,我们需要在应用启动阶段通过它来配置身份验证功能。
AuthenticationOptions对象允许我们对身份验证做整体配置,这个配置主要体现为:支持的身份验证方案列表;指定默认身份验证方案、默认登录时用的身份验证方案、默认注销方案...。我们可以通过AddAuthentication(Action<AuthenticationOptions>)重载来进行配置。参考如下代码:

 services.AddAuthentication(authenticationOptions=> {
    authenticationOptions.AddScheme<CookieAuthenticationHandler>("cookie", "显示名cookie");//添加Cookie验证方案
    authenticationOptions.AddScheme<JwtBearerHandler>("jwt","显示名jwtToken");//添加JwtBearer验证方案
    authenticationOptions.DefaultAuthenticateScheme = "cookie";//默认的验证方案
    //...其它配置
 });

AddScheme添加身份验证方案。

public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder)
{
    var builder = new AuthenticationSchemeBuilder(name);
    configureBuilder(builder);
    _schemes.Add(builder);
}

name方案名;configureBuilder允许我们提供委托对方案进行配置
添加的这些方案最终会被存储到AuthenticationSchemeProvider供其使用
另外DefaultAuthenticateScheme、DefaultSignInScheme、DefaultSignOutScheme..看名字也晓得它是说当我们调用某个步骤未指定使用那个方案是的默认选择

AuthenticationSchemeOptions(某个具体的身份验证方案的选项)

在上述身份验证处理的多个步骤中会用到一些选项数据,比如基于cookie的身份验证 cookeName、有效时长、再比如请求时从cookie中解析得到用户标识后回调选项中的某个回调函数,允许我们的代码向调试中添加额外数据,或者干脆替换整个标识。
所以身份验证选项用来允许我们控制AuthenticationHandler的执行。不同的身份验证方式有不同的选项对象,比如:CookieAuthenticationOptionsJwtBearerOptions.. 它们直接或间接继承AuthenticationSchemeOptions

我们以cookie认证举例

CookieAuthenticationOptions就是针对这个cookie身份验证方案的选项对象

 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie("方案名","显示名",opt=> {
       opt.SlidingExpiration = true;
       //其它设置...
 });

AuthenticationResult

AuthenticateResult用来表示认证的结果

public class AuthenticateResult
{
    public AuthenticationTicket Ticket { get; protected set; }

    public bool Succeeded => Ticket != null;
    public ClaimsPrincipal Principal => Ticket?.Principal;
    public AuthenticationProperties Properties => Ticket?.Properties;
    public Exception Failure { get; protected set; }
    public bool None { get; protected set; }
    public static AuthenticateResult Success(AuthenticationTicket ticket) => new AuthenticateResult() { Ticket = ticket };
    public static AuthenticateResult NoResult() => new AuthenticateResult() { None = true };
    public static AuthenticateResult Fail(Exception failure) => new AuthenticateResult() { Failure = failure };
    public static AuthenticateResult Fail(string failureMessage) => new AuthenticateResult() { Failure = new Exception(failureMessage) };
}

AuthenticationTicket

AuthenticateResult主要包含一个核心属性 AuthenticationTicket,可以把它看成是一个经过认证后颁发的证书

public class AuthenticationTicket
{ 
    public string AuthenticationScheme { get; private set; }
    public ClaimsPrincipal Principal { get; private set; }
    public AuthenticationProperties Properties { get; private set; }
}

当我们创建完ClaimsPrincipal对象后,需要将它生成一个用户票据并颁发给用户,然后用户拿着这个票据,便可以访问受保持的资源,而在ASP.NET Core中,用户票据用AuthenticationTicket来表示,如在Cookie认证中,其认证后的Cookie值便是对该对象序列化、加密后的结果,如下:

var properties = new AuthenticationProperties();
var ticket = new AuthenticationTicket(principal, properties, "myScheme");
// 加密 序列化
var token = Protect(ticket);

最后,我们可以将票据(token)写入到Cookie中,或是也可以以JSON的形式返回让客户端自行保存,由于我们对票据进行了加密,可以保证在网络中安全的传输而不会被篡改。

AuthenticationProperties

AuthenticationProperties 属性用来表示证书颁发的相关信息,如token、颁发时间,过期时间,重定向地址等等:

public class AuthenticationProperties
{
    public IDictionary<string, string> Items { get; }

    public string RedirectUri
    {
        get
        {
            string value;
            return Items.TryGetValue(RedirectUriKey, out value) ? value : null;
        }
        set
        {
            if (value != null) Items[RedirectUriKey] = value;
            else
            {
                if (Items.ContainsKey(RedirectUriKey)) Items.Remove(RedirectUriKey);
            }
        }
    }
    ...
}

在上面最开始介绍的HttpContext中的 GetTokenAsync 扩展方法便是对AuthenticationProperties的扩展:

public static class AuthenticationTokenExtensions
{
    private static string TokenNamesKey = ".TokenNames";
    private static string TokenKeyPrefix = ".Token.";

    public static void StoreTokens(this AuthenticationProperties properties, IEnumerable<AuthenticationToken> tokens) {}
    public static bool UpdateTokenValue(this AuthenticationProperties properties, string tokenName, string tokenValue) {}
    public static IEnumerable<AuthenticationToken> GetTokens(this AuthenticationProperties properties) { }

    public static string GetTokenValue(this AuthenticationProperties properties, string tokenName)
    {
        var tokenKey = TokenKeyPrefix + tokenName;
        return properties.Items.ContainsKey(tokenKey) ? properties.Items[tokenKey] : null;
    }

    public static Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string tokenName) 
        => auth.GetTokenAsync(context, scheme: null, tokenName: tokenName);

    public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName)
    {
        var result = await auth.AuthenticateAsync(context, scheme);
        return result?.Properties?.GetTokenValue(tokenName);
    }
}

IClaimsTransformation

IClaimsTransformation 用来对由我们的应用程序传入的 ClaimsPrincipal 进行转换,它只定义了一个 Transform 方法:

public interface IClaimsTransformation
{
    Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal);
}

其默认实现,不做任何处理,直接返回。它适合于全局的为 ClaimsPrincipal 添加一些预定义的声明,如添加当前时间等,然后在DI中把我们的实现注册进去即可。

posted @ 2019-12-17 15:29  .Neterr  阅读(747)  评论(0编辑  收藏  举报