前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NETCore编程实现基本认证

ASP.NETCore编程实现基本认证

作者头像
有态度的马甲
发布2020-04-16 15:00:10
9280
发布2020-04-16 15:00:10
举报
文章被收录于专栏:精益码农

HTTP基本认证

在HTTP中,HTTP基本认证(Basic Authentication)是一种允许浏览器或其他客户端程序使用(用户名,口令)请求资源的身份验证方式,不要求cookie,session identifier、login page等标记或载体。

  • 所有浏览器据支持HTTP基本认证协议
  • 基本身证原理不保证传输凭证的安全性,仅被based64编码,并没有encrypted或者hashed,一般部署在互信的内网,在公网上应用BA协议通常与https结合。

BA标准协议

BA协议的实施主要依靠约定的请求头/响应头, 典型的浏览器和服务器的BA认证流程:

① 浏览器请求应用了BA的网站,服务端响应一个401认证失败响应码,并写入WWW-Authenticate响应头,指示服务端支持BA协议。

代码语言:javascript
复制
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="our site"

或在初次请求时发送正确Authorization标头,从而避免被质询

② 客户端以based64(用户名:口令) 作为Authorization标头值,重新发送请求:

Authorization: Basic userid:password

认证的范围与realm相关,准确的realm由服务端定义,因为服务端可能有多个不同的realm.

> 浏览器客户端,对于WWW-Authenticate响应头弹出了口令输入窗。

BA编程实践

ASP.NET Core网站利用FileServerMiddleware将部分路径映射到文件资源,对该资源访问路径应用Http基本认证。

服务端实现BA认证

① 实现基本认证Handler:认证、无口令质询、质询失败逻辑

代码语言:javascript
复制
# ......
namespace EqidManager.Services
{
    public static class BasicAuthenticationScheme
    {
        public const string DefaultScheme = "Basic";
    }
    public class BasicAuthenticationOption:AuthenticationSchemeOptions
    {
        public string Realm { get; set; }
        public string UserName { get; set; }
        public string UserPwd { get; set; }
    }

    public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOption>
    {
        private readonly BasicAuthenticationOption authOptions;
        public BasicAuthenticationHandler(
            IOptionsMonitor<BasicAuthenticationOption> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
            authOptions = options.CurrentValue;
        }

        /// <summary>
        /// 认证逻辑
        /// </summary>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
                return AuthenticateResult.Fail("Missing Authorization Header");
            string username, password;
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
                 username = credentials[0];
                 password = credentials[1];
                 var isValidUser= IsAuthorized(username,password);
                if(isValidUser== false)
                    return AuthenticateResult.Fail("Invalid username or password");
            }
            catch
                return AuthenticateResult.Fail("Invalid Authorization Header");
            var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier,username),
                new Claim(ClaimTypes.Name,username),
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
            return await Task.FromResult(AuthenticateResult.Success(ticket));
        }

        /// <summary>
        /// 质询
        /// </summary>
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Options.Realm}\"";
            await base.HandleChallengeAsync(properties);
        }

        /// <summary>
        /// 认证失败
        /// </summary>
        protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
           await base.HandleForbiddenAsync(properties); 
        }
        private bool IsAuthorized(string username, string password)
        {
            return username.Equals(authOptions.UserName, StringComparison.InvariantCultureIgnoreCase)
                   && password.Equals(authOptions.UserPwd);
        }
    }
}

② 实现BasicAuthenticationMiddleware: 要求对HttpContext应用BA Scheme。

代码语言:javascript
复制
// HTTP基本认证Middleware
 public static class BasicAuthentication
 {
        public static void UseBasicAuthentication(this IApplicationBuilder app)
        {
            app.UseMiddleware<BasicAuthenticationMiddleware>();
        }
 }
public class BasicAuthenticationMiddleware
{
     private readonly RequestDelegate _next;
     private readonly ILogger _logger;
     public BasicAuthenticationMiddleware(RequestDelegate next, ILoggerFactory LoggerFactory)
     {
        _next = next;
        _logger = LoggerFactory.CreateLogger<BasicAuthenticationMiddleware>();
     }
     public async Task Invoke(HttpContext httpContext, IAuthenticationService authenticationService)
     {
       var authenticated = await authenticationService.AuthenticateAsync(httpContext, BasicAuthenticationScheme.DefaultScheme);
       _logger.LogInformation("Access Status:" + authenticated.Succeeded);
       if (!authenticated.Succeeded)
       {
           await authenticationService.ChallengeAsync(httpContext, BasicAuthenticationScheme.DefaultScheme, new AuthenticationProperties { });
           return;
       }
       await _next(httpContext);
     }
}

③ ASP.NET Core 添加BA Scheme , 为待认证资源路径启用BA中间件,注意这里使用UseWhen插入中间件。

代码语言:javascript
复制
services.AddAuthentication(BasicAuthenticationScheme.DefaultScheme)
                .AddScheme<BasicAuthenticationOption, BasicAuthenticationHandler>(BasicAuthenticationScheme.DefaultScheme,null);
app.UseWhen(
            predicate:x => x.Request.Path.StartsWithSegments(new PathString(_protectedResourceOption.Path)),
            configuration:appBuilder => { appBuilder.UseBasicAuthentication();}
    );

现可在浏览器测试:

进一步思考?

以上是浏览器在BA协议中的行为:可尝试程序自动向服务端发起BA请求,需要的同学看博客园源码。

That's All . BA认证是常见的基础认证协议,文章期待以清晰的方式传递协议原理和编程实现,要的同学阅读原文。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 精益码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档