1. 程式人生 > >關於ASP.NET MVC 5 的一種簡單的身份驗證方式:FormsAuthentication.Authenticate

關於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