概览

在HTTP中,基本认证(Basic access authentication,简称BA认证)是一种用来允许网页浏览器或其他客户端程序在请求资源时提供用户名和口令形式的身份凭证的一种登录验证方式,不要求cookie,session identifier、login page等标记或载体。

优点:基本上所有流行的网页浏览器都支持BA认证。

缺点:明文传输密钥和口令(容易被拦截); 没有对服务器返回的信息提供保护。

https://en.wikipedia.org/wiki/Basic_access_authentication

特征

基本身份认证原理不保证传输凭证的安全性,他们仅仅只是被based64编码,并没有encrypted或者hashed。

一般部署在可信任网络,在公开网络中部署BA认证通常要与https结合使用。

因为基本身份认证字段在HTTP Header 中发送,所以web browser需要在一个合理的时间内缓存凭据,缓存策略因浏览器不同而异,IE默认缓存15 分钟。 HTTP不会为客户端提供一个logout方法,但是有很多方法在浏览器中可以清除缓存凭据。

<script>document.execCommand('ClearAuthenticationCache');</script>

IIS-WebSite-身份认证:

BA认证的标准协议

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

1.浏览器请求一个需要身份认证的页面,但是没有提供用户名和口令。这通常是用户在地址栏输入一个URL,或是打开了一个指向该页面的链接。

2.服务端响应一个401应答码,并提供一个认证域。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="180.76.176.244"

3.接到应答后,浏览器显示该认证域并提示输入用户名和口令。此时用户可以选择确定或取消。

4.用户输入了用户名和口令后,浏览器会在原请求方式上增加认证消息头Authorization:{base64encode(username+":"+password)},然后重新发送再次尝试。

// 下面的例子显示:认证双方约定在编码字符串前加上了此次认证的方式和一个空格: Basic:

Authorization: Basic RmlzYWRzYWQ6YXNkYWRz

5.服务器接受该认证凭据并返回页面;如果用户凭据非法或无效,服务器可能再次返回401应答码,客户端可以再次提示用户输入口令。

注意:
① 客户端用户名,口令之间用冒号分隔
② 服务端返回认证域,可能会指定凭据字符串的编码方式:

WWW-Authenticate: Basic realm="User Visible Realm", charset="UTF-8"

此时客户端在准备凭据字符串的时候,要使用指定的charset编码凭据

③ 客户端未认证时,服务端需要给客户端必要的认证域提示,效果如下:

BA认证编程实践

项目中利用StaticFileMiddleware+DirectoryBrowserMiddleware做了一个网页文件服务器, 现对该文件服务器设置BA认证。

ASP.NET Core服务端实现BA认证:

  1. 实现基本身份认证Handler, 包含认证方式、认证挑战的提示

  2. asp.netcore 添加认证中间件

  3. 注册认证中间件

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

       /// <summary>
       /// BA认证过程
       /// </summary>
       /// <returns></returns>
       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[];
                password = credentials[];
                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>
       /// 重写该方法以体现身份验证挑战(401)时发生时提示
       /// </summary>
       /// <param name="properties"></param>
       /// <returns></returns>
       protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
       {
           Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Options.Realm}\",charset=\"utf-8\"";
           await base.HandleChallengeAsync(properties);
       }

       /// <summary>  进行BA认证,此处不关注该方法
       /// override the method to influence what happens when an forbidden response (403)
       /// </summary>
       /// <param name="properties"></param>
       /// <returns></returns>
       protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
       {
          await base.HandleForbiddenAsync(properties);
       }
       //  BA认证:账户、密码匹配逻辑
       private bool IsAuthorized(string username, string password)
       {
           return username.Equals(authOptions.UserName, StringComparison.InvariantCultureIgnoreCase)
                  && password.Equals(authOptions.UserPwd);
       }
   }
// 添加BA认证计划services.AddAuthentication(BasicAuthentication.DefaultScheme)
                .AddScheme<BasicAuthenticationOption, BasicAuthenticationHandler>(BasicAuthentication.DefaultScheme,null);
// 这里我使用UseWhen方式注册认证中间件: 对该网站部分资源 开启基本身份认证app.UseWhen(
            predicate:x => x.Request.Path.StartsWithSegments(new PathString(_protectedResourceOption.Path)),
            configuration:appBuilder => appBuilder.UseAuthentication()
    );

客户端:

  1. 添加认证请求Handler
  2. 以上述Handler 配置命名HtttpClient
    /// <summary>
    /// BA认证请求Handler    /// </summary>
    public class BasicAuthenticationClientHandler : HttpClientHandler
    {
        public static string BAHeaderNames = "authorization";
        private RemoteBasicAuth _remoteAccount;

        public BasicAuthenticationClientHandler(RemoteBasicAuth remoteAccount)
        {
            _remoteAccount = remoteAccount;
            AllowAutoRedirect = false;
            UseCookies = true;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorization = $"{_remoteAccount.UserName}:{_remoteAccount.Password}";
            var authorizationBased64 = "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(authorization));
            request.Headers.Remove(BAHeaderNames);
            request.Headers.Add(BAHeaderNames, authorizationBased64);
            return base.SendAsync(request, cancellationToken);
        }
    }
// 配置命名HttpClientservices.AddHttpClient("eqid-ba-request", x =>
          x.BaseAddress = new Uri(_proxyOption.Scheme +"://"+ _proxyOption.Host+":"+_proxyOption.Port ) )
   .ConfigurePrimaryHttpMessageHandler(y => new BasicAuthenticationClientHandler(_remoteAccount){} )
   .SetHandlerLifetime(TimeSpan.FromMinutes())
   .AddPolicyHandler(GetRetryPolicy());

That's All .  BA认证是随处可见的基础认证协议,本文期待以最清晰的方式帮助你理解协议,最优雅的方式提供编程实践思路。

作者:Julian_酱

感谢您的认真阅读,如有问题请大胆斧正,如果您觉得本文对你有用,不妨右下角点个或加关注。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置注明本文的作者及原文链接,否则保留追究法律责任的权利。

ASP.NET Core实现 随处可见的基本身份认证的更多相关文章

  1. ASP.NET Core[源码分析篇] - Authentication认证

    原文:ASP.NET Core[源码分析篇] - Authentication认证 追本溯源,从使用开始 首先看一下我们通常是如何使用微软自带的认证,一般在Startup里面配置我们所需的依赖认证服务 ...

  2. 【asp.net core 系列】13 Identity 身份验证入门

    0. 前言 通过前两篇我们实现了如何在Service层如何访问数据,以及如何运用简单的加密算法对数据加密.这一篇我们将探索如何实现asp.net core的身份验证. 1. 身份验证 asp.net ...

  3. ASP.NET Core Identity 实战(3)认证过程

    如果你没接触过旧版Asp.Net Mvc中的 Authorize 或者 Cookie登陆,那么你一定会疑惑 认证这个名词,这太正式了,这到底代表这什么? 获取资源之前得先过两道关卡Authentica ...

  4. ASP.NET Core WebAPI中使用JWT Bearer认证和授权

    目录 为什么是 JWT Bearer 什么是 JWT JWT 的优缺点 在 WebAPI 中使用 JWT 认证 刷新 Token 使用授权 简单授权 基于固定角色的授权 基于策略的授权 自定义策略授权 ...

  5. asp.net core 同时添加Identity和Bearer认证

    是这样的,网上介绍的Oauth认证一般都是授权服务器和资源服务器分开,但是我只想在一个网站中使用asp.net core自带的Identity认证给用户访问网站用,同时提供一些api接口通过Token ...

  6. 【.NET Core微服务实战-统一身份认证】开篇及目录索引

    简介 ​ 学习.NETCORE也有1年多时间了,发现.NETCORE项目实战系列教程很少,都是介绍开源项目或基础教程,对于那些观望的朋友不能形成很好的学习思路,遇到问题怕无法得到解决而不敢再实际项目中 ...

  7. ASP.Net Core 3.0 中使用JWT认证

    JWT认证简单介绍     关于Jwt的介绍网上很多,此处不在赘述,我们主要看看jwt的结构.     JWT主要由三部分组成,如下: HEADER.PAYLOAD.SIGNATURE HEADER包 ...

  8. asp.net core系列 60 Ocelot 构建服务认证示例

    一.概述 在Ocelot中,为了保护下游api资源,用户访问时需要进行认证鉴权,这需要在Ocelot 网关中添加认证服务.添加认证后,ReRoutes路由会进行身份验证,并使用Ocelot的基于声明的 ...

  9. 重新拾取:ASP.NET Core WebApi 使用Swagger支持授权认证

    园子里已经有很多.NET Core 集成Swagger的文章,但对于使用授权的介绍蛮少的. public static class SwaggerServiceExtensions { public ...

随机推荐

  1. Heap

    #include using namespace std; int heap[100010],cnt=0; void put(int x) { cnt++; heap[cnt]=x; int now= ...

  2. leetCode刷题(找出数组里的两项相加等于定值)

    最近被算法虐了一下,刷一下leetcode,找找存在感 如题: Given an array of integers, return indices of the two numbers such t ...

  3. CSS学习笔记二:css 画立体图形

    继上一次学了如何去运用css画平面图形,这一次学如何去画正方体,从2D向着3D学习,虽然有点满,但总是一个过程,一点一点积累,然后记录起来. Transfrom3D 在这一次中运用到了一下几种属性: ...

  4. 利用css实现hover动态效果

    .font em:hover { font-size: 2em } .font strong:hover { font-weight: normal } .font span:hover { colo ...

  5. 回忆一下我的运维时期 关于Impact的架构服务器集群

    Impact EDMP平台  Email Direct Marketing Platfrom   电子邮件营销平台 EDM 是 Email Direct Marketing 的缩写,即电子邮件营销,简 ...

  6. 嵌入Python | 调用Python模块中有参数的函数

    开发环境Python版本:3.6.4 (32-bit)编辑器:Visual Studio CodeC++环境:Visual Studio 2013 需求说明前一篇<在C++中嵌入Python|调 ...

  7. AngularJS 最常用的几种功能

    AngularJS 最常用的几种功能 2017-04-13 吐槽阿福 互联网吐槽大会 第一 迭代输出之ng-repeat标签ng-repeat让table ul ol等标签和js里的数组完美结合 1 ...

  8. Net Core动态加载webservice/WCF

    1.动态加载的目的 前端时间和顺丰对接了个项目(PS:顺丰的开发对外能力真的是掉粉),用的webservice 测试时用的无固定IP访问,正式版需要固定IP访问,我的理解是web服务都是全网络可以访问 ...

  9. 【转】java中equal与==的区别 其中有个缓冲区,需要注意

    转自http://www.cnblogs.com/dolphin0520/p/3592500.html 在学Java时,可能会经常碰到下面的代码: 1 String str1 = new String ...

  10. 你不知道的JavaScript--Item16 for 循环和for...in 循环的那点事儿

    大家都知道在JavaScript中提供了两种方式迭代对象: for 循环: for..in循环: 1.for循环 不足: 在于每次循环的时候数组的长度都要去获取: 终止条件要明确: 在for循环中,你 ...