關於ASP.NET MVC 5 的一種簡單的身份驗證方式:FormsAuthentication.Authenticate
在ASP.NET MVC 5中,身份驗證分別有三種方式。分別為使用FormsAuthentication、MemberShip和Identity進行驗證。
(PS:本系列的邏輯程式碼請勿直接用於生產,請自己多加一層抽象後再投入使用)
為了展示這三種方式,我們先新建一個MVC專案。
為了方便,我們建立一個類用來驗證身份。
public class AccountHelper { [Required] public string UserName { get; set; } [Required]public string Password { get; set; } }
方式一:FormsAuthentication
在使用任何一種驗證方法之前,我們需要建立一個檢視用以進行登入,然後在這個檢視中建立一個form用以提交資料
public ActionResult Login() { return View(); }
檢視程式碼:
@model Authority.Models.AccountHelper @{ ViewBag.Title= "Index"; } <h2>Index</h2> <p>Please log in to access the administration area:</p> @using (Html.BeginForm()) { @Html.ValidationSummary() <div class="form-group"> <label>User name</label> @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) </div> <div class="form-group"> <label>Password</label> @Html.TextBoxFor(m => m.Password, new { @class = "form-control" }) </div> <input type="submit" value="Log In" class="btn-primary" /> }
然後我們需要在Web.config中對驗證方式進行配置。
<authentication mode="Forms"> <forms loginUrl="~/FormsAuthentication/Login" timeout="2880"> <credentials passwordFormat="Clear"> <user name="admin" password="secret" /> </credentials> </forms> </authentication>
接著需要建立一個方法用來接收form元素所提交的資料,對資料進行驗證並保留Cookie,對驗證成功後返回另一個檢視。
[HttpPost] public ActionResult Login(AccountHelper helper,string returnUrl) { if (ModelState.IsValid) { if (FormsAuthentication.Authenticate(helper.UserName, helper.Password)) { FormsAuthentication.SetAuthCookie(helper.UserName,false); return Redirect(returnUrl ?? Url.Action("AfterAuthority",helper)); } else { ModelState.AddModelError("", "Error account or password"); return View(); } } return View(); }
對驗證成功後的檢視進行簡單的定義。
[Authorize] public ActionResult AfterAuthority(AccountHelper helper) { return View(helper); }
@model Authority.Models.AccountHelper @{ ViewBag.Title = "AfterAuthority"; } <h2>AfterAuthority</h2> <p>Your Username is: @Model.UserName</p> <p>Your Password is: @Model.Password</p>
其中Login方法的returnUrl 用於未在 Login 處進行驗證而直接訪問帶有 [Authorize] 修飾的檢視時,在驗證後可返回原來訪問的地址。假如我對一個Edit檢視(該檢視後面會定義)進行訪問它會直接返回至Login方法並帶有ReturnUrl欄位:
而在驗證之後通過returnUrl直接返回至Edit方法。
那麼問題來了,要是我想增加更多的使用者或者修改現有的賬戶該怎麼辦呢?這時候我們就需要對Web.config直接進行修改了。但在這之前我們需要有一個物件用來儲存我們現在賬戶的資訊,因此我們需要對Login方法也進行一定的修改:
static AccountHelper account=new AccountHelper(); [HttpPost] //returnUrl 用於未在 Login 處進行驗證而直接訪問帶有 [Authorize] 修飾的檢視時,在驗證後可返回原來訪問的地址。 public ActionResult Login(AccountHelper helper,string returnUrl) { if (ModelState.IsValid) { if (FormsAuthentication.Authenticate(helper.UserName, helper.Password)) { account.UserName = helper.UserName; account.Password = helper.Password; FormsAuthentication.SetAuthCookie(helper.UserName,false); return Redirect(returnUrl ?? Url.Action("AfterAuthority",account)); } else { ModelState.AddModelError("", "Error account or password"); return View(); } } return View(); }
於是,我們可以建立一個用於修改現有賬戶的檢視了。
[Authorize] public ActionResult Edit() { return View(account); }
@model Authority.Models.AccountHelper @{ ViewBag.Title = "Edit"; } <h2>Edit</h2> <p>Your Password is: @Model.Password</p> <div>Edit your account</div> @using (Html.BeginForm()) { <div class="form-group"> <p>Your Username is: @Model.UserName</p> <p>Change it to: @Html.EditorFor(m => m.UserName)</p> </div> <br /> <div class="form-group"> <p>Your Password is: @Model.Password</p> <p>Change it to: @Html.EditorFor(m => m.Password)</p> </div> <input type="submit" value="submit"/> }
[HttpPost] [Authorize] public ActionResult Edit(AccountHelper changedAccount) { XmlDocument doc=new XmlDocument(); //獲得配置檔案的全路徑 string strFileName = AppDomain.CurrentDomain.BaseDirectory+"Web.Config"; doc.Load(strFileName); XmlNodeList nodes = doc.GetElementsByTagName("user"); for (int i = 0; i < nodes.Count; i++) { string _name = nodes[i].Attributes["name"]==null? "":nodes[i].Attributes["name"].Value; if (_name==account.UserName) { nodes[i].Attributes["name"].Value = changedAccount.UserName; nodes[i].Attributes["password"].Value = changedAccount.Password;
//清除Cookie FormsAuthentication.SignOut(); break; } } //將修改後的 Web.config 進行儲存 doc.Save(strFileName); return View("AfterAuthority",changedAccount); }
重新整理之後:
以修改前的賬戶登入:
至此修改現有賬號的任務完成。
而新增賬戶不過將XML元素的修改改成插入。
[Authorize] public ActionResult Add() { return View(); } [HttpPost] [Authorize] public ActionResult Add(AccountHelper newAccount) { XmlDocument doc = new XmlDocument(); //獲得配置檔案的全路徑 string strFileName = AppDomain.CurrentDomain.BaseDirectory + "Web.Config"; doc.Load(strFileName); XmlNodeList nodes = doc.GetElementsByTagName("user"); XmlNode credentials = nodes[0].ParentNode; XmlNode child = nodes[0].Clone(); child.Attributes["name"].Value = newAccount.UserName; child.Attributes["password"].Value = newAccount.Password; credentials.AppendChild(child); //將修改後的 Web.config 進行儲存 doc.Save(strFileName); return View("Create", newAccount); }
Add檢視:
@model Authority.Models.AccountHelper @{ ViewBag.Title = "Add"; } <h2>Create Account</h2> @using (Html.BeginForm()) { <div class="form-group"> <p>UserName: @Html.EditorFor(m => m.UserName)</p> <p>Password: @Html.EditorFor(m => m.Password)</p> </div> <input type="submit" value="submit"/> }
Create檢視:
@model Authority.Models.AccountHelper @{ ViewBag.Title = "Create Success"; } <h2>Successfully</h2> <p>UserName: @Model.UserName</p> <p>Password: @Model.Password</p>
但是問題又來了,總不能什麼人都可以對賬戶進行修改或增添使用者吧,因此我們需要使用自定義特性,通過繼承AuthorizeAttribute獲得和AuthorizeAttribute同樣的效果,通過繼承IAuthenticationFilter對賬戶進行過濾,在此我們限制Add方法只有admin這一賬戶可以使用:
public class AdminAccountAttribute : AuthorizeAttribute, IAuthenticationFilter { public void OnAuthentication(AuthenticationContext filterContext) { IIdentity identity = filterContext.Principal.Identity; if (identity.Name!="admin" || !identity.IsAuthenticated) { filterContext.Result = new HttpUnauthorizedResult(); } } public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) { if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult) { filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary() { {"controller","FormsAuthentication" }, { "action","Login"}, {"returnUrl",filterContext.HttpContext.Request.RawUrl } }); } } }
然後將該修飾用於Add之上:
[AdminAccount] public ActionResult Add() { return View(); } [HttpPost] [AdminAccount] public ActionResult Add(AccountHelper newAccount) {
於是我們便只有admin使用者可以對Web.config進行增添使用者的操作了。
通過一個非admin使用者訪問Add頁面,返回Login頁面。
通過admin使用者訪問Add頁面則返回Add頁面。
至此,一個完整的登入系統達成。如果需要什麼額外的邏輯也可自行新增。
但是由於使用FormsAuthentication.Authenticate需要使用Web.config,在Web.config直接對賬戶和密碼進行配置,過於繁瑣、不安全且擴充套件性差,對配置檔案進行顯式的修改更是增添了風險;同時,如果需要對使用者的賬號或者密碼進行限制我們便需要進行硬編碼。該方法早已被棄用,之後ASP.NET推出了Membership.ValidateUser方法解決這一情況,而現在更是有了Identity用來更進一步地提高擴充套件性。
原始碼地址:
https://github.com/NanaseRuri/FormsAuthentication