随处可见的基本身份认证 & 编程实现(C#)

  • A+
所属分类:腾讯分分彩

基本身份认证Basic Authentication,简称BA认证

概览

       HTTP基本身份验证是一种最简单的web资源访问控制的技术,客户端携带username、password去请求服务器资源,不要求cookie,session identifier、login page等标记或载体。

基本身份认证过程主要在header特定字段中体现,不需要handshakes。

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

特征

        基本身份认证协议不保证传输凭证的安全性,他们仅仅只是被based64编码,并没有encrypted或者hashed,因此Basic Authentication为了保密性通常与https结合一起使用。

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

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

 

BA认证的标准协议

客户端

客户端使用Authorization Request header 发送认证凭据,凭据生成方式如下:

  1. username、password字段以冒号分隔(username不能包含冒号)

  2. string ---> 字节

  3. Baseed64编码

  4. 在编码后字符串前加上Authorization 方法和一个空格

Authorization: Basic RmlzYWRzYWQ6YXNkYWRz
服务端

当服务端设定了基本身份认证服务时,服务端要给未通过认证请求适当的认证提示:
response status code:401 (Unauthorized)
repsonse header:WWW-Authenticate

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

        客户端表现如下图:

  随处可见的基本身份认证 & 编程实现(C#)

BA认证的日常应用、实现

日常应用

       IIS自带基本身份验证,以下是在IIS website中配置使用基本身份认证:

随处可见的基本身份认证 & 编程实现(C#)

 

以.NetCore实践BA认证

服务端:

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

  2. .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[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>
       /// 重写该方法以体现身份验证挑战(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); 
       }

       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启用一个中间件: 对某些路径开启BA认证
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); } }

 

// 配置命名HttpClient
services.AddHttpClient("eqid-ba-request", x => x.BaseAddress = new Uri(_proxyOption.Scheme +"://"+ _proxyOption.Host+":"+_proxyOption.Port ) ) .ConfigurePrimaryHttpMessageHandler(y => new BasicAuthenticationClientHandler(_remoteAccount){} ) .SetHandlerLifetime(TimeSpan.FromMinutes(2)) .AddPolicyHandler(GetRetryPolicy());

 

以上应该是从协议、场景、实现讲述了BA认证的全部知识点 。

 

--------------如果你觉得文章对你有价值,请点赞或者关注;如有问题请大胆斧正,蟹蟹--------------~~。。~~--------------