在ASP.NET MVC專案新增了開發偵錯專用的Controller,某些Action想限定從localhost存取,以免遭到誤用。逐一在Action加入檢查IP邏輯是種做法,但如此有點浪費ASP.NET MVC強大的擴充性,就好比提著子彈上膛的M16步槍上戰場,不扣板機卻拿槍托狂敲敵人的頭,不免有暴殄天物之憾。
ASP.NET MVC有個Filter機制(中文翻成篩選器),在Filter可自訂執行Action時要一併觸發的邏輯,為Action加上[FilterName] Attribute,便可在該Action之前或之後插入自訂邏輯。這種概念很適合用來實現Log記錄、權限控管、Exception處理... 等等通用性任務。而IAuthorizationFilter介面專司權限管控,符合IP篩選的安全性質,因此我的構想是寫個類別,實做IAuthorizationFilter介面的OnAuthorization()方法,由傳入的AuthorizationContext取得UserHostAddress判斷來源IP,若HttpRequest不是來自本機(localhost)即拋出錯誤,就實現了Action只開放本機存取的效果。
FilterAttribute完成後,任何Action只要加上該FilterAttribute宣告,就會自動套用上述檢查限定本機存取,非常簡便易用。
以下是完整程式範例,為了增加應用彈性,我特別再抽出一層AllowedIpOnlyAttribute,宣告時傳入允許存取的IP清單;而LocalhostOnlyAttribute繼承AllowedIpOnlyAttribute,將允許IP寫死::1(IPv6)及127.0.0.1,成為AllowedIpOnlyAttribute的特例,做到限定本機存取。如此一魚兩吃,一次獲得兩種Filter,適用於不同情境。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyWeb.Models
{
publicclass AllowedIpOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
privatestring[] ipList = newstring[] {};
//建構式接收以逗號或分號分隔的IP清單,限定存取來源
//TODO: 如要方便事後修改,可擴充成由config讀取IP清單,但會增加被破解風險
public AllowedIpOnlyAttribute(string allowedIps)
{
ipList = allowedIps.Split(',', ';');
}
#region IAuthorizationFilter Members
publicvoid OnAuthorization(AuthorizationContext filterContext)
{
//實作OnAuthorization,當來源IP不在清單上,彈出錯誤
string clientIp = filterContext.HttpContext.Request.UserHostAddress;
if (!ipList.Contains(clientIp))
thrownew ApplicationException("Disallowed Client IP!");
}
#endregion
}
//限定本機存取為AllowedIpOnlyAttribute的特殊情境,限定IP=::1或127.0.0.1
publicclass LocalhostOnlyAttribute : AllowedIpOnlyAttribute
{
public LocalhostOnlyAttribute()
: base("::1;127.0.0.1")
{
}
}
}
接著,見識Filter便利性的時刻來了,在Action加上AllowedIpOnly或LocalhostOnly,Action立刻變成限定特定IP或本機IP才能使用! 很方便吧?
[AllowedIpOnly("192.168.1.100")]
public ActionResult IpOnly()
{
return Content("IpOnly");
}
[LocalhostOnly]
public ActionResult LocalhostOnly()
{
return Content("LocalHostOnly");
}