这篇文章以实现一个Basic认证来了解下在 .NET CORE 下面如何去实现认证。

  首先可以肯定的是认证实现是基于 Middlerware 管道实现的,官方源码地址:https://github.com/aspnet/Security。可以看到官方已经实现了jwt、oauth、google等诸多第三方认证,其原理今天我们就不在这里介绍。

   下面我们来实现Basic认证。

  AuthenticationSchemeOptions。 负责初始化参数配置。这里我们额外需要一个验证用户的委托。代码如下:

    public class BasicOption : AuthenticationSchemeOptions
{
public BasicOption()
: base()
{
Events = new BasicEvents();
} public Func<string, string, bool> ValidateUser { get; set; }
public new BasicEvents Events
{
get { return (BasicEvents)base.Events; }
set { base.Events = value; }
}
}

  BasicDefault。定义一些基本常量

    public static class BasicDefault
{
public const string AuthenticationScheme = "Basic";
public const string DisplayName = "Basic";
}

  ResultContext<T>。用于认证流程中上下文扩展

    public class BasicTokenValidatedContext : ResultContext<BasicOption>
{
public BasicTokenValidatedContext(HttpContext context, AuthenticationScheme scheme, BasicOption options)
: base(context, scheme, options)
{
} }

  BasicEvents。用于认证流程中各类自定义事件触发,在这里我们定义了一个 验证成功后事件,用于客户端自定义设置

    public class BasicEvents
{
public Func<BasicTokenValidatedContext, Task> OnTokenValidated { get; set; } = context => Task.CompletedTask; public virtual Task TokenValidated(BasicTokenValidatedContext context) => OnTokenValidated(context);
}

  AuthenticationHandler<T>。这里认证流程中的核心部分,HandleAuthenticateAsync 用于处理认证。HandleChallengeAsync 用于处理认证失败后续Challenge

    public class BasicHandler : AuthenticationHandler<BasicOption>
{
private const string KEY_AUTHORIZATION = "authorization";
private const string KEY_SPLIT = ":"; protected new BasicEvents Events
{
get => (BasicEvents)base.Events;
set => base.Events = value;
} public BasicHandler(IOptionsMonitor<BasicOption> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
} protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string authorization = Request.Headers[KEY_AUTHORIZATION];
if (string.IsNullOrEmpty(authorization))
{
Logger.LogInformation("请求头authorization为空,目标路径{0}", Request.Path);
return AuthenticateResult.NoResult();
}
string token = string.Empty;
if (authorization.StartsWith(BasicDefault.AuthenticationScheme + " ", StringComparison.CurrentCultureIgnoreCase))
{
token = authorization.Substring(BasicDefault.AuthenticationScheme.Length).Trim();
}
if (string.IsNullOrEmpty(token))
{
Logger.LogInformation("无效的请求头authorization,目标路径{0}", Request.Path);
return AuthenticateResult.NoResult();
} var checkUser = Options.ValidateUser;
if (checkUser == null)
{
Logger.LogInformation("Basic TokenValidator不能,目标路径{0}", Request.Path);
return await Task.FromResult(AuthenticateResult.NoResult());
} try
{
var data = Encoding.UTF8.GetString(Convert.FromBase64String(token));
if (string.IsNullOrEmpty(data)) throw new Exception("basic token 格式错误"); string[] array = data.Split(KEY_SPLIT.ToCharArray());
if (array.Length != ) throw new Exception("basic token 格式错误"); var username = array[];
var password = array[];
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) throw new Exception("basic token 格式错误"); if (!checkUser(username, password))
{
Logger.LogInformation("token 验证失败");
return AuthenticateResult.Fail("token 验证失败");
} var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, username)
}; var principer = new ClaimsPrincipal(new ClaimsIdentity(claims, BasicDefault.AuthenticationScheme));
var validatedContext = new BasicTokenValidatedContext(Context, Scheme, Options)
{
Principal = principer
}; await Events.TokenValidated(validatedContext); validatedContext.Success(); return validatedContext.Result;
}
catch (Exception ex)
{
Logger.LogDebug(token + " validate failed: " + ex.Message);
return AuthenticateResult.Fail(ex.Message);
} } protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
var authResult = await HandleAuthenticateOnceSafeAsync(); Response.Headers.Add(HeaderNames.WWWAuthenticate, BasicDefault.AuthenticationScheme);
Response.StatusCode = ;
if (authResult.Failure != null && !string.IsNullOrEmpty(authResult.Failure.Message))
{
var byteMsg = System.Text.Encoding.Default.GetBytes(authResult.Failure.Message);
Response.Body.Write(byteMsg, , byteMsg.Length);
} await base.HandleChallengeAsync(properties);
}
}

  BasicExtensions。用于提供注册到.NET CORE的方法。

    public static class BasicExtensions
{
public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder)
=> builder.AddBasic(BasicDefault.AuthenticationScheme, _ => { }); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicOption> configureOptions)
=> builder.AddBasic(BasicDefault.AuthenticationScheme, configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicOption> configureOptions)
=> builder.AddBasic(authenticationScheme, displayName: BasicDefault.DisplayName, configureOptions: configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicOption> configureOptions)
{
return builder.AddScheme<BasicOption, BasicHandler>(authenticationScheme, displayName, configureOptions);
}
}

  以上就是完成Basic认证所有的方法。我们发现居然没有涉及到 任何Middlerware的部分。实际原因是 官方实现了默认的 Authentication,里面有一个 IAuthenticationRequestHandler 的集合,我们创建的 AuthenticationHandler<T> 扩展就会加入该集合中,Authentication 会负责对集合中的 每一个 Handler 进行处理。源码部分如下:

            var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();

            foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())

            {

                var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;

                if (handler != null && await handler.HandleRequestAsync())

                {

                    return;

                }

            }

  最后我们将Basic认证注册到.NET CORE中

            services.AddAuthentication(BasicDefault.AuthenticationScheme)
.AddBasic(options =>
{
options.ValidateUser = (username, password) =>
{
var clients = Configuration.GetSection("Clients").Get<List<ClientOptions>>();
if (clients == null || clients.Count == ) return false; return clients.Exists(x => string.Equals(x.Appkey, username, StringComparison.CurrentCultureIgnoreCase)
&& string.Equals(x.Appsecret, password, StringComparison.CurrentCultureIgnoreCase));
};
//options.Events = new AspNetCore.Authentication.Basic.Events.BasicEvents();
options.Events.OnTokenValidated = context =>
{
if (context.Principal.Identity.IsAuthenticated)
{
var clients = Configuration.GetSection("Clients").Get<List<ClientOptions>>();
if (clients == null || clients.Count == ) return Task.CompletedTask;
var appkey = context.Principal.Identity.Name;
var actions = clients.Single(x => string.Equals(x.Appkey, appkey, StringComparison.CurrentCultureIgnoreCase)).Actions;
context.Properties.SetParameter("actions", actions);
} return Task.CompletedTask;
};
});

  别忘了 在 Configure 方法中加入

app.UseAuthentication();

  好了,我们的Basic认证完成了~~

后续问题,测试过程中发现 即使认证不通过的话 action 也能正常访问,需要配合 Authorize 才能触发 Challenge。这里我对Authentication和Authorization又增加了一点疑惑,按道理 前者负责确认 用户,后者负责确认 用户权限,但如果用户确认为非法的情况下,为什么还要等到Authorization这块来处理??

  

.NET CORE之Authentication的更多相关文章

  1. ASP.NET Core [4]: Authentication(笔记)

    原文连接:http://www.cnblogs.com/RainingNight/p/authentication-in-asp-net-core.html 在现代应用程序中,认证已不再是简单的将用户 ...

  2. Angular调用Asp.net Core JWT Authentication接口

    基本思路是调用登录接口,获取token,使用token请求其他JWT接口: getHomeDetails(): Observable<HomeDetails> { let headers ...

  3. Asp.Net Core Authentication Middleware And Generate Token

    .mytitle { background: #2B6695; color: white; font-family: "微软雅黑", "宋体", "黑 ...

  4. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  5. [转]ASP.NET Core 中的那些认证中间件及一些重要知识点

    本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...

  6. 在ASP.NET Core 中使用Cookie中间件

    在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ...

  7. ASP.NET Core 使用Cookie验证身份

    ASP.NET Core 1.x提供了通过Cookie 中间件将用户主体序列化为一个加密的Cookie,然后在后续请求中验证Cookie并重新创建主体,并将其分配给HttpContext.User属性 ...

  8. ASP.NET Core 开源GitServer 实现自己的GitHub

    ASP.NET Core 2.0 开源Git HTTP Server,实现类似 GitHub.GitLab. GitHub:https://github.com/linezero/GitServer ...

  9. .net core 同时实现网站管理员后台、会员、WebApi登录及权限控制

    我们在开网站信息系统时,常常有这样几个角色,如后台的管理员,前台的会员,以及我们各种应用的WebAPI 都需要进行登录操作及权限控制,那么在.net core如何进行设计呢. 首先我使用的是.net ...

随机推荐

  1. 多对多表创建、forms组件、cookie与session

    多对多表的三种创建方式 1.全自动(较为推荐) 优势:不需要你手动创建第三张表 不足:由于第三张表不是你手动创建的,所以表字段是固定的无法扩展 class Book(models.Model): ti ...

  2. QTP基本循环正常遍历(代码方式实现)

    0 环境 系统环境:win7 1 操作(正常遍历篇) 1.1 代码前看 systemutil.Run "D:\Program Files (x86)\HP\QuickTest Profess ...

  3. POJ2352 Stars [树状数组模板]

    题意:输入一n颗星星的x,y坐标,给定判断level的标准,即某颗星星左下边(不高于它,不超过他,相当于以他为基准的第三象限)星星的数目为level, 输出level从0到n的星星个数. //poj2 ...

  4. HDU-1061-Rightmost Digit(快速幂)

    快速幂(本代码中的^表示次幂不是异或) Accepted 1061 0MS 1368K 679 B G++ #include "bits/stdc++.h" using names ...

  5. spring配置ConcurrentMap实现缓存

    spring本身内置了对Cache的支持,本次记录的是基于Java API的ConcurrentMap的CacheManager配置. 1.xml文件中增加命名空间 <beans xmlns=& ...

  6. CRISPR/Cas9|InParanoid|orthoMCL|PanOCT|pan genome|meta genome|Core gene|CVTree3|

    生命组学: 泛基因组学:用于描述一个物种基因组,据细菌基因组动力学,因为细菌的基因漂移使得各个细菌之间的基因组差异很大,(单个细菌之间的基因组差异是以基因为单位的gain&loss,而人类基因 ...

  7. 修改 commit message

    本文为原创文章,转载请标明出处 目录 修改上一条提交的 commit message 修改之前提交的 commit message 1. 修改上一条提交的 commit message git com ...

  8. Dangling meta character '' near index 0

    1.replaceAll()方法报错 对字符串使用replaceAll()方法替换 * ? + / | 等字符的时候会报以下异常 Dangling meta character '*' near in ...

  9. SpringDataJpa2

    1.SpringDataJpa的扩展 - 抽取 创建一个BaseRepository接口然后去继承JpaRepository和JpaSpecificationExecutor 然后在里面写我们自己想要 ...

  10. hexo博客的学习笔记

    这篇文章主要的作用是作为 .md文件打开,内部的格式为一个初学者对hexo以及markdown语法运用的笔记 1.Hexo的写文格式 最开始为文章的属性部分,以三横杠-开始,-结束.里面记录了文章的标 ...