2014年8月19日 星期二

ASP.NET MVC 用自訂Action Filter來做Log

參考這篇

張小呆的碎碎唸

稍微改成log到資料庫並記錄下當時所處理的資料.

public class LogActionFilterAttribute : ActionFilterAttribute
    {
        public string ControllerName { get; set; }
        public string ActionName { get; set; }

        private ApplicationDbContext db = new ApplicationDbContext();

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            string uid = filterContext.HttpContext.User.Identity.Name.ToString();
            string originController = filterContext.RouteData.Values["controller"].ToString();
            string originAction = filterContext.RouteData.Values["action"].ToString();
            string originArea = String.Empty;
            if (filterContext.RouteData.DataTokens.ContainsKey("area"))
                originArea = filterContext.RouteData.DataTokens["area"].ToString();

          //這邊利用傳遞參數的名稱來判定要如取出相關資料與以記錄, 所以Action的參數名稱就必須要有可鑑別性

            string result = "";
            if (filterContext.ActionParameters.ContainsKey("empv"))
            {
                empEditViewModels viewModel = (empEditViewModels)filterContext.ActionParameters["empv"];
                result = viewModel.emp1.eid + viewModel.emp1.cname + " 職稱: " + viewModel.emp1.title + " 部門編號: " + viewModel.emp1.dept;

            };
            if (filterContext.ActionParameters.ContainsKey("emp"))
            {
                emp emp1 = (emp)filterContext.ActionParameters["emp"];
                result = emp1.eid + emp1.cname + " 職稱: " + emp1.title + " 部門編號: " +emp1.dept;

            };
            if (filterContext.ActionParameters.ContainsKey("id"))
            {
                int id1 = (int)filterContext.ActionParameters["id"];
                result = " 序號: " + id1.ToString();

            };

          //紀錄相關資料到資料庫

            actlog logmodel = new actlog()
            {
                App = ControllerName+originController,
                Act = ActionName+originAction,
                Pepo = String.IsNullOrEmpty(SessionHelper.RealName) ? uid : SessionHelper.RealName,
                Ext ="進入網頁 : "+result,
                Tm = DateTime.Now              
            };
            db.actlogs.Add(logmodel);
            db.SaveChanges();
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            string uid = filterContext.HttpContext.User.Identity.Name.ToString();
            actlog logmodel = new actlog()
            {

                App = ControllerName,
                Act = ActionName,
                Pepo = String.IsNullOrEmpty(SessionHelper.RealName) ? uid : SessionHelper.RealName,
                Ext = "傳回的動作結果執行完成",
                Tm = DateTime.Now
            };
            db.actlogs.Add(logmodel);
            db.SaveChanges();
        }
    }

在control中使用範例 :

[HttpPost]
        [ValidateAntiForgeryToken]
        [LogActionFilter(ControllerName = "員工資料管理", ActionName = "新增完成")]
        public ActionResult Create([Bind(Include = "id,eid,cname,dept,title")] emp emp, int? page, int? itemsPerPage, string sortOrder, string currentFilter)
        {
            ViewBag.DropDownList = new SelectList(db.deps, "Id", "title");
            ViewBag.CurrentPage = page;
            ViewBag.CurrentItemsPerPage = itemsPerPage;
            ViewBag.OldSortParm = sortOrder;
            ViewBag.CurrentFilter = currentFilter;
            if (ModelState.IsValid)
            {
                db.emps.Add(emp);
                db.SaveChanges();
                return RedirectToAction("Index", new { page = ViewBag.CurrentPage, itemsPerPage = ViewBag.CurrentItemsPerPage, sortOrder = ViewBag.OldSortParm, searchString = "", CurrentFilter = ViewBag.CurrentFilter });
            }
            return View(emp);
        }


紀錄結果 :

在authentication mode="Forms"下使用 membership defaultProvider="ADMembershipProvider", 搭配Fluent Security的測試及如何使用AD群組來做權限控管

參考這篇,
Fluent Security - 在MVC集中管理頁面權限的套件

真的不錯用, 但受到Form登入無法直接使用 AD的群組當Roles的影響.

所以只能用上

configue.ForAllControllers().Ignore();

configue.For<HomeController>().DenyAnonymousAccess();

configue.For<HomeController>().DenyAuthenticatedAccess();

而一直找不到使用角色的辦法..

configue.For<HomeController>(hc => hc.Contact()).RequireAnyRole("Everyone");

只好回頭用自訂 action filter的辦法來處理 AD的Group.

1.加入參考
C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.DirectoryServices.AccountManagement.dll

2. Using System.DirectoryServices.AccountManagement;

3.使用者登入時取回AD Group資料塞入Session, 給自訂Action Filter及View使用.
 [HttpPost]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (!this.ModelState.IsValid)
        {
            return this.View(model);
        }

        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            //取回AD Group資料塞入Session
            var context = new PrincipalContext(ContextType.Domain, "Adimmune");
            var userPrincipal = UserPrincipal.FindByIdentity(context,
                                                 IdentityType.SamAccountName,
                                                 model.UserName);
            var UGS = userPrincipal.GetAuthorizationGroups();
            string uGroup = "";
            foreach (var ug in UGS)
                uGroup = uGroup + ug.Name + ";";
            SessionHelper.UserGroup = uGroup;

            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if (this.Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return this.Redirect(returnUrl);
            }

            return this.RedirectToAction("Index", "Home");
        }

        this.ModelState.AddModelError(string.Empty, "您提供的Windows帳號或密碼並不正確,如有疑問請與資訊人員聯絡.");

        return this.View(model);
    }

//!!切記要在LogOff時清空SessionHelper.UserGroup,否則會影響權限控管
 public ActionResult LogOff()
    {
        SessionHelper.UserGroup = "";
        SessionHelper.RealName = "";
        FormsAuthentication.SignOut();

        return this.RedirectToAction("Index", "Home");
    }

4.自訂Action Filter.
 public class AuthorizeADAttribute : AuthorizeAttribute
    {
        public string Groups { get; set; }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
                /* Return true immediately if the authorization is not
                locked down to any particular AD group */
                if (String.IsNullOrEmpty(Groups))
                    return true;

                // Get the AD groups
                var groups = Groups.Split(',').ToList<string>();

                // Verify that the user is in the given AD group (if any)
                foreach (var group in groups)
                            if (SessionHelper.UserGroup.Contains(group+";"))
                            return true;                
            return false;
        }

5. Control使用範例

 [AuthorizeAD(Groups = "IT")]
 public class empsController : Controller
    {
..................................
}

6 View使用範例
if (Session["UserGroup"].ToString().Contains("webAdmin01;"))
                    {
                        <ul class="nav pull-right">
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown">後台管理<b class="caret"></b></a>
                                <ul class="dropdown-menu">
                                    <li><a href="@Url.Action("Index", "emps")"><i class="icon-off"></i>員工</a></li>
                                    <li><a href="@Url.Action("Index", "deps")"><i class="icon-off"></i>部門</a></li>
                                    <li><a href="@Url.Action("Index", "exms")"><i class="icon-off"></i>評核類別</a></li>
                                    <li><a href="@Url.Action("Index", "ots")"><i class="icon-off"></i>評核時間</a></li>
                                    <li><a href="@Url.Action("Index", "ts")"><i class="icon-off"></i>評核紀錄</a></li>
                                    <li><a href="@Url.Action("Index", "actlogs")"><i class="icon-off"></i>Logs</a></li>
                                </ul>
                            </li>
                        </ul>
                    }