隨處可見的基本身份認證 & 編程實現(C#)

基本身份認證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"

        客戶端表現如下圖:

  

BA認證的日常應用、實現

日常應用

       IIS自帶基本身份驗證,以下是在IIS website中配置使用基本身份認證:

 

以.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認證的全部知識點 。

 

--------------如果你覺得文章對你有價值,請點贊或者關注;如有問題請大膽斧正,蟹蟹--------------~~。。~~--------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章