NetCore配置BasicAuth用戶認證功能

一、Http BasicAuth 概述

Http BasicAuth 即 Http 基本認證,其最簡單的實質是,服務器容許客戶端攜帶帳號密碼/加密認證請求頭來訪問受保護資源,例如:算法

  1. curl http://username:password@localhost/api/users 實現訪問受保護資源
  2. Http請求頭中,加入Authorization, 值爲 Basic base64str 實現訪問受保護資源
base64str 實際爲 username:password 字符串通過base64算法編碼後的字符串

基本的交互流程以下圖所示api

image-20200906080253594

二、項目搭建

根據如下步驟,建立一個基本的NetCore項目(基於NetCore3.1版本)瀏覽器

image-20200906075058998

image-20200906075249985

根據上面三個圖所示,咱們便可建立好一個最基本的NetCore項目服務器

3. BasicAuth配置

3.1 自定義AuthenticationHandler

經過自定義AuthenticationHandler實現BasicAuth認證,實現代碼以下:app

using ApiBasicAuth.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace ApiBasicAuth.Security
{
    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {

        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                Response.Headers.Add("WWW-Authenticate", @"Basic realm='Secure Area'");
                return AuthenticateResult.Fail("Missing Authorization Header");
            }
            User user = null;
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                if (username.Equals("admin") && password.Equals("password"))
                {
                    user = new User { Id=1, Username = "admin", Birthday = DateTime.Now };
                }
            }
            catch
            {
                // Base64字符串解碼失敗
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            if (user == null)
                return AuthenticateResult.Fail("Invalid Username or Password");

            var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }
    }
}

其中有幾點值得比較注意:curl

  1. Response.Headers.Add("WWW-Authenticate", @"Basic realm='Secure Area'"); 在認證失敗時,返回這個請求頭,則是告訴瀏覽器,要求彈出認證信息輸入窗口(瀏覽器自身功能),若沒返回此請求頭,則瀏覽器不會彈出自帶帳號/密碼輸入窗口,直接顯示401
  2. if (username.Equals("admin") && password.Equals("password")) 在示例代碼中,直接固定只支持admin帳號,在實際中,因該是根據諸如UserService等服務層,來判斷用戶帳號信息
  3. var claims = new[] {
         new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
         new Claim(ClaimTypes.Name, user.Username),
     };

    在示例中,默認只是給當前用戶設置了兩個基本屬性,在實際中,咱們能夠根據用戶的角色/權限等信息,注入對應的Claim,如:async

    var roles = new List<string>();
    foreach (var item in roles)
    {
        claims.Append(new Claim(ClaimTypes.Role, item));
    };

3.2 修改NetCore認證配置

public void ConfigureServices(IServiceCollection services)中,增長以下配置ide

services.AddAuthentication("BasicAuthentication")
 .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)中,增長以下配置:ui

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

至此,項目的認證服務配置,搭建完成編碼

四、認證功能驗證

經過上面的章節,咱們已經配置好認證服務,下面則是開始構建Controller,來校驗認證是否生效。以下圖,咱們便攜一個最基本的Controller

namespace ApiBasicAuth.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class UserController : ControllerBase
    {

        [HttpGet]
        [AllowAnonymous]
        public IActionResult NoSecurity() {

            return Ok("NoSecurity");
        }


        [HttpGet]
        [Authorize]
        public IActionResult Security()
        {
            return Ok("Security");
        }
    }
}

其中

  1. /api/User/Security 須要認證後才能訪問
  2. /api/User/NoSecuirty不須要認證就能訪問
  1. [AllowAnonymous] 表示接口任何人都能訪問
  2. [Authorize] 標識接口須要登錄認證後,才能進行訪問

咱們開始進行校驗:

  1. 訪問/api/User/Security 不攜帶認證信息

image-20200906144148642

請求返回401,代表未認證,則表明咱們配置的認證服務已生效。

  1. 訪問/api/User/Security攜帶認證信息

image-20200906144324772

能夠看到,返回Security,代表已經認證經過

  1. 訪問/api/User/NoSecurity不攜帶認證信息

image-20200906144553003

能夠看到,對於無需認證的接口,不攜帶認證信息也能獲取內容。

五、結語

至此,BasicAuth 認證服務就配置驗證完成,其實咱們能夠經過這個例子,能夠衍生推導出如何實現JWT認證服務,基本原理一致,只是須要稍微修改BasicAuthenticationHandler配置便可,後續這邊也會編寫NetCore實現自定義JWT認證服務相關文章。

歡迎關注公衆號,獲取最新文章信息

image

相關文章
相關標籤/搜索