【3】.net MVC 使用IPrincipal進行Form登錄即權限驗證


1.在MVC項目中添加用戶類,可以根據實際項目需求添加必要屬性

public class UserData
    {
        /// <summary>
        /// ID
        /// </summary>
        public int UserId { get; set; }

        /// <summary>
        /// 用戶名
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// 角色ID列表
        /// </summary>
        public List<int> Roles { get; set; }
    }

 

 

2.添加類Principal實現IPrincipal接口

    public class Principal : IPrincipal
    {
        public IIdentity Identity { get; private set;}

        public UserData Account { get; set; }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="ticket"></param>
        /// <param name="account"></param>
        public Principal(FormsAuthenticationTicket ticket, UserData account)
        {
            if (ticket == null)
                throw new ArgumentNullException("ticket");
            if (account == null)
                throw new ArgumentNullException("UserData");

            this.Identity = new FormsIdentity(ticket);
            this.Account = account;
        }

        public bool IsInRole(string role)
        {
            if (string.IsNullOrEmpty(role))
                return true;
            if (this.Account == null || this.Account.Roles == null)
                return false;
            return role.Split(',').Any(q => Account.Roles.Contains(int.Parse(q)));
        }
    }

 


IPrincipal接口有對象Identity已經需要實現驗證角色方法IsInRole()。在我們的實現類中添加了"用戶信息(UserData)"屬性Account。

構造函數中進行了初始化,第一個對象為Form驗證的票據對象,下面ticket會攜帶用戶信息一起保存進cookie中。

3.創建存儲cookie和讀取cookie的類

    /// <summary>
    /// 寫入cookie和讀取cookie
    /// </summary>
    public class HttpFormsAuthentication
    {
        //將用戶信息通過ticket加密保存到cookie
        public static void SetAuthenticationCoolie(UserData account, int rememberDay = 0)
        {
            if (account == null)
                throw new ArgumentNullException("account");

            //序列化account對象
            string accountJson = JsonConvert.SerializeObject(account);
            //創建用戶票據
            var ticket = new FormsAuthenticationTicket(1, account.UserName, DateTime.Now, DateTime.Now.AddDays(rememberDay), false, accountJson);
            //加密
            string encryptAccount = FormsAuthentication.Encrypt(ticket);

            //創建cookie
            var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptAccount)
            {
                HttpOnly = true,
                Secure = FormsAuthentication.RequireSSL,
                Domain = FormsAuthentication.CookieDomain,
                Path = FormsAuthentication.FormsCookiePath
            };

            if (rememberDay > 0)
                cookie.Expires = DateTime.Now.AddDays(rememberDay);

            //寫入Cookie
            HttpContext.Current.Response.Cookies.Remove(cookie.Name);
            HttpContext.Current.Response.Cookies.Add(cookie);
        }

        //獲取cookie並解析出用戶信息
        public static Principal TryParsePrincipal(HttpContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            HttpRequest request = context.Request;
            HttpCookie cookie = request.Cookies[FormsAuthentication.FormsCookieName];
            if (cookie == null || string.IsNullOrEmpty(cookie.Value))
            {
                return null;
            }
            //解密coolie值
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

            UserData account = JsonConvert.DeserializeObject<UserData>(ticket.UserData);
            return new Principal(ticket, account);
        }

    }

 


存儲cookie時將用戶信息序列化后的字符串accountJson由ticket其攜帶加密后保存入cookie中,具體的accountJson被賦值給FormsAuthenticationTicket的UserData屬性。

可看到解析時將ticket.UserData反序列化后得到了原始的用戶信息對象,然后生成Principal對象。

解析cookie得到Principal對象的方法TryParsePrincipal,下面會在發起請求時用到,而返回的Principal對象被賦值給HttpContext.User。

4.在Global.asax中注冊Application_PostAuthenticateRequest事件,保證權限驗證前將cookie中的用戶信息取出賦值給User

protected void Application_PostAuthenticateRequest(object sender, System.EventArgs e)
        {
            HttpContext.Current.User =
                HttpFormsAuthentication.TryParsePrincipal(HttpContext.Current);
        }

 

5.集成AuthorizeAttribute特性類並重寫AuthorizeCore,HandleUnauthorizedRequest方法

    public class FormAuthorizeAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// 先進入此方法,此方法中會調用 AuthorizeCore 驗證邏輯,驗證不通過會調用 HandleUnauthorizedRequest 方法
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        /// <summary>
        /// 權限驗證
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var user = httpContext.User as Principal;
            if (user != null)
                return user.IsInRole(base.Roles);
            return false;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            //驗證不通過,直接跳轉到相應頁面,注意:如果不是喲娜那個以下跳轉,則會繼續執行Action方法
            filterContext.Result = new RedirectResult("~/Login/Index");
        }
    }

 



AuthorizeCore與HandleUnauthorizedRequest方法均是在方法OnAuthorization中調用,AuthorizeCore驗證不通過才會調用HandleUnauthorizedRequest方法。

將驗證代碼在AuthorizeCore中實現,驗證不通過的邏輯在HandleUnauthorizedRequest方法中實現。

6.添加LoginController實現登錄邏輯

namespace MVCAuthorizeTest.Controllers
{
    public class LoginController : Controller
    {
        [AllowAnonymous]
        // GET: Login
        public ActionResult Index(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Index(string name, string password, bool rememberMe, string returnUrl)
        {
            var account = new UserData()
            {
                UserName = name,
                UserId = 110,
                Roles = new List<int>() { 1, 2, 3 }
            };
            HttpFormsAuthentication.SetAuthenticationCoolie(account, rememberMe ? 7 : 0);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

        // POST: /Account/LogOff
        [HttpPost]
        public ActionResult LogOff()
        {
            System.Web.Security.FormsAuthentication.SignOut();
            return RedirectToAction("Index", "Home");
        }
    }
}

 

7.對需要驗證的controller或action添加特性標簽

    [FormAuthorize(Roles = "1,2")]
    public class HomeController : Controller
    {
        [FormAuthorize]
        public ActionResult Index()
        {
            return View();
        }
    }

 


如圖

8.在添加FilterConfig中添加全局注冊filter,減少每個action分別設置。如果有不需要驗證的頁面,添加[AllowAnonymous]特性即可

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            //全局注冊filter
            filters.Add(new FormAuthorizeAttribute());
        }
    }

 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM