这篇文章以实现一个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. ofo小黄车推出车身商业化广告,能自救成功吗?

    ofo终于还是扛不住了!作为之前与摩拜相抗衡的巨头,ofo其实也是最被看好的共享单车企业之一.可惜共享单车行业的窘境,终究不是成为巨头后就能拯救的.摩拜被美团收购后,ofo就成为巨头垂涎的目标.数十亿 ...

  2. Spring常见的两种增强方式

    一.编程式增强 不借助spring的配置,通过自己实例化对象来实现的增强方式 创建增强类,需要实现你需要的增强接口,(只有实现了该接口,这个类就是一个通知)) /** * 增强类 */ public ...

  3. git 代理配置方式

    # 设置ss git config --global http.proxy 'socks5://127.0.0.1:1080' git config --global https.proxy 'soc ...

  4. OpenCV 输入输出XML和YAML文件

    #include <opencv2/core/core.hpp> #include <iostream> #include <string> using names ...

  5. 在HADOOP中使用MRUNIT进行单元测试

    在HADOOP中使用MRUNIT进行单元测试 前提 1. 了解JUnit4.x的使用. 2. 了解Mock的概念在单元测试中的应用. 3. 了解Hadoop中MapReduce的编程模型. 如果您对J ...

  6. mybatis generator 使用教程(生成带注释的实体类)

    引言: 最近的一个项目,由于数据库表巨多,导致需要创建N多个java实体.dao.mapper.xml映射文件,如果均使用纯手工编写,无疑需要耗费大量时间和精力.于是上网学习了mybatis gene ...

  7. Ionic3学习笔记(十六)上传头像至图床

    本文为原创文章,转载请标明出处 个人做的开源 Demo 登录注册模块采用的是 Wilddog 野狗通讯云的身份认证服务,不得不说各方面和 Google 收购的 Firebase 很像,十分简单易用.其 ...

  8. Ionic3学习笔记(十四)使用 videogular2 实现视频播放以及遇到的一些问题

    本文为原创文章,转载请标明出处 目录 使用 videogular2 安装 增加图标.字体支持 导入 module 举个例子 遇到的问题 iOS 端自动进入全屏播放 Android 端 autoplay ...

  9. ES查询实例

    注:转载自https://www.cnblogs.com/yjf512/p/4897294.html 作者:叶剑锋 elasticsearch 查询(match和term) es中的查询请求有两种方式 ...

  10. 20190407-ORID

    2019-04-07 Objective 关于今天的课程,你记得什么? 给代码建立分支的操作 完成了什么? 完成了rails101前6节 Relective 今天的高峰是什么? 成功完成rails10 ...