當我們撰寫WebAPI的時候,最擔心的就是這個WebAPI會被其他不明人士、或是未經授權的人任意的使用
透過http的基本驗證的方式,可以作到允許的使用者及帳號才能存取我們自己寫的WebAPI
一般來說,透過http的基本驗證方式,就是在http請求的Header上,加入一個名為Authorization的Header,其內容為"Basic " + Base64Encode(username:password)
在Client端把伺服器給予允許存取的username與password,經過Base64的編碼後,放入至名為Authorization的Header中,隨著http的請求一起送至Server端
而Server端在取得這樣的驗證資訊後,驗證是否為允許存取的資訊,並回傳訊息
以這樣的驗證方式,若是直接在WebAPI的程序中直接加入[Authorize]的Attribute,就會以預設的驗證方式進行驗證
所以在本篇文章中,我們會透過覆寫驗證功能的作法,將驗證功能另外獨立作出一個AOP的Attribute,讓每一個WebAPI的程序直接加入Attribute就可以進行驗證,而不需額外加寫程式碼
首先,先在WebAPI的專案中,加入一個BasicAuthenticationIdentity的類別庫,並將下面的程式碼加入至該類別庫中
using System.Security.Principal;
public class BasicAuthenticationIdentity : GenericIdentity
{
public BasicAuthenticationIdentity(string name, string password)
: base(name, "Basic")
{
this.Password = password;
}
public string Password { get; set; }
}
這個類別庫主要的目的是將要驗證的帳號與密碼,建立一個新的物件,並繼承GenericIdentity這個類別,代表要傳入的資訊為一個泛型的使用者物件
接下來,在專案中加入BasicAuthenticationFilter這個類別庫,並將下面程式碼加入至該類別庫中
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Net.Http;
using System.Threading;
using System.Security.Principal;
using System.Net;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthenticationFilter : AuthorizationFilterAttribute
{
bool Active = true;
public BasicAuthenticationFilter()
{ }
public BasicAuthenticationFilter(bool active)
{
Active = active;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
if (Active)
{
var identity = ParseAuthorizationHeader(actionContext);
if (identity == null)
{
Challenge(actionContext);
return;
}
if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
{
Challenge(actionContext);
return;
}
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
base.OnAuthorization(actionContext);
}
}
protected virtual bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false;
return true;
}
protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
{
string authHeader = null;
var auth = actionContext.Request.Headers.Authorization;
if (auth != null && auth.Scheme == "Basic")
authHeader = auth.Parameter;
if (string.IsNullOrEmpty(authHeader))
return null;
authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));
var tokens = authHeader.Split(':');
if (tokens.Length < 2)
return null;
return new BasicAuthenticationIdentity(tokens[0], tokens[1]);
}
void Challenge(HttpActionContext actionContext)
{
var host = actionContext.Request.RequestUri.DnsSafeHost;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
}
}
這一段類別庫程式碼最主要的內容,在於覆寫OnAuthorization這一個事件,置換成我們自己要進行帳號與密碼驗證動作的程式碼
接著在專案中,加入第三個類別庫ApiBasicAuthenticationFilter,並加入下面的程式碼內容
using System.Web.Http.Controllers;
public class ApiBasicAuthenticationFilter : BasicAuthenticationFilter
{
public ApiBasicAuthenticationFilter()
{ }
public ApiBasicAuthenticationFilter(bool active) : base(active)
{ }
protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
// 在這裡加上帳號密碼的驗證,可以從資料庫取出資料進行比對
string strUserName = username;
string strPassword = password;
bool blIsAuthorize = false;
/* 作一個假的驗證,測試用
if (strUserName == "maduka" && strPassword == "ABCDE")
blIsAuthorize = true;
*/
return blIsAuthorize;
}
}
這一個類別庫繼承了剛剛建立的BasicAuthenticationFilter,並覆寫了OnAuthorizeUser這一個事件,在OnAuthorizeUser這一個事件中,加入我們自己必須進行的帳號密碼驗證動作,而呼叫WebAPI的Client端程式傳入的帳號與密碼,就是在這裡進行資料的驗證,當然驗證的動作也可以與資料庫進行比較,也就是說這裡完全就是由我們自己決定該怎麼驗證帳號資訊了
最後,我們在WebAPI的控制器中,加入ApiBasicAuthenticationFilter這個Attribute,這樣在Server端的WebAPI,就完成了驗證的準備動作
// GET api/values
[SwaggerOperation("GetAll")]
[ApiBasicAuthenticationFilter]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
然後實際執行一次呼叫WebAPI的動作,此時會發現,由於透過一般WebAPI的呼叫方式,也就是不帶入任何基本驗證資訊的http請求,WebAPI是會要求http的基本驗證的
若是透過Postman或是其他REST API的測試工具,將Authorization的基本驗證資訊放入後作出的請求,就可以順利的進行WebAPI的呼叫
WebAPI的使用授權及驗證一直以來都是WebAPI開發上一個很重要的課題,透過這樣基本驗證功能的覆寫,並使用簡易的AOP屬性,就可以很方便的快速套用在每一個WebAPI上