目錄javascript
1. 介紹html
2. UMLjava
2.1 實體類UML圖web
2.2 業務相關UML圖redis
3.2 業務相關源代碼cookie
3.3 相關控制器源代碼mvc
1. 介紹
1.1 nopcommerce介紹
nopcommerce是國外的一個高質量的開源b2c網站系統,基於EntityFramework4.0和MVC3.0,使用Razor模板引擎,有很強的插件機制,包括支付配送功能都是經過插件來實現的.
nopcommerce主要模塊從上往下Nop.Web、Nop.Admin、Nop.Web.Framework、Nop插件、Nop.Services、Nop.Core、Nop.Data。引用的第三方模塊EntityFramework,Autofac(控制反轉,即依賴注入),telerik.extern.mvc(後臺管理用的界面,2.0後開始使用)
1.2 文章來由
我以前對ASP.NET MVC4的用戶驗證和權限管理這塊內容理解的不深入,一次偶然的機會使得我下載到了nopcommerce的源代碼,該項目中含有訪問控制功能,其功能頁面如圖1-1所示。
爲了進一步理解MVC4的用戶驗證和權限管理的內容,我用了兩天時間窺視了nopcommerce的訪問控制功能,略有心得體會,並以我本身的理解內容寫了一個Demo:AclDemo,該項目將在下一篇分析以及提供源代碼。
圖1-1
2. UML
該小節主要提供了nopcommerce項目中的訪問控制功能的UML類圖以及類圖說明,在製做UML類圖的過程當中,我簡化了UML類圖,這樣是爲了讓UML內容不冗餘。簡化後的UML類圖只顯示了和訪問控制功能有關的內容。
2.1 實體類UML圖
2.2 業務相關UML圖
3. 核心代碼分析
3.1 實體類源代碼
1 /// <summary> /// Represents a customer role /// </summary> public partial class CustomerRole : BaseEntity { private ICollection<PermissionRecord> _permissionRecords; /// <summary> /// Gets or sets the customer role name /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets a value indicating whether the customer role is active /// </summary> public bool Active { get; set; } /// <summary> /// Gets or sets a value indicating whether the customer role is system /// </summary> public bool IsSystemRole { get; set; } /// <summary> /// Gets or sets the customer role system name /// </summary> public string SystemName { get; set; } /// <summary> /// Gets or sets the permission records /// </summary> public virtual ICollection<PermissionRecord> PermissionRecords { get { return _permissionRecords ?? (_permissionRecords = new List<PermissionRecord>()); } protected set { _permissionRecords = value; } } }
1 /// <summary> /// Represents a permission record /// </summary> public partial class PermissionRecord : BaseEntity { private ICollection<CustomerRole> _customerRoles; /// <summary> /// Gets or sets the permission name /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the permission system name /// </summary> public string SystemName { get; set; } /// <summary> /// Gets or sets the permission category /// </summary> public string Category { get; set; } /// <summary> /// Gets or sets discount usage history /// </summary> public virtual ICollection<CustomerRole> CustomerRoles { get { return _customerRoles ?? (_customerRoles = new List<CustomerRole>()); } protected set { _customerRoles = value; } } }
3.2 業務相關源代碼
1 /// <summary> /// Customer service interface /// </summary> public partial interface ICustomerService { #region Customers /// <summary> /// Get customer by username /// </summary> /// <param name="username">Username</param> /// <returns>Customer</returns> Customer GetCustomerByUsername(string username); #endregion }
2
1 /// <summary> /// Customer service /// </summary> public partial class CustomerService : ICustomerService { #region Constants #endregion #region Fields private readonly IRepository<Customer> _customerRepository; private readonly IRepository<CustomerRole> _customerRoleRepository; #endregion #region Ctor public CustomerService( IRepository<Customer> customerRepository, IRepository<CustomerRole> customerRoleRepository,) { this._customerRepository = customerRepository; this._customerRoleRepository = customerRoleRepository; } #endregion #region Methods #region Customers /// <summary> /// Get customer by username /// </summary> /// <param name="username">Username</param> /// <returns>Customer</returns> public virtual Customer GetCustomerByUsername(string username) { if (string.IsNullOrWhiteSpace(username)) return null; var query = from c in _customerRepository.Table orderby c.Id where c.Username == username select c; var customer = query.FirstOrDefault(); return customer; } #endregion #endregion }
1 public partial interface IAuthenticationService { void SignIn(Customer customer, bool createPersistentCookie); void SignOut(); Customer GetAuthenticatedCustomer(); }
1 public partial class FormsAuthenticationService : IAuthenticationService { private readonly HttpContextBase _httpContext; private readonly ICustomerService _customerService; private readonly CustomerSettings _customerSettings; private readonly TimeSpan _expirationTimeSpan; private Customer _cachedCustomer; /// <summary> /// Ctor /// </summary> /// <param name="httpContext">HTTP context</param> /// <param name="customerService">Customer service</param> /// <param name="customerSettings">Customer settings</param> public FormsAuthenticationService(HttpContextBase httpContext, ICustomerService customerService, CustomerSettings customerSettings) { this._httpContext = httpContext; this._customerService = customerService; this._customerSettings = customerSettings; this._expirationTimeSpan = FormsAuthentication.Timeout; } public virtual void SignIn(Customer customer, bool createPersistentCookie) { var now = DateTime.UtcNow.ToLocalTime(); var ticket = new FormsAuthenticationTicket( 1 /*version*/, _customerSettings.UsernamesEnabled ? customer.Username : customer.Email, now, now.Add(_expirationTimeSpan), createPersistentCookie, _customerSettings.UsernamesEnabled ? customer.Username : customer.Email, FormsAuthentication.FormsCookiePath); var encryptedTicket = FormsAuthentication.Encrypt(ticket); var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); cookie.HttpOnly = true; if (ticket.IsPersistent) { cookie.Expires = ticket.Expiration; } cookie.Secure = FormsAuthentication.RequireSSL; cookie.Path = FormsAuthentication.FormsCookiePath; if (FormsAuthentication.CookieDomain != null) { cookie.Domain = FormsAuthentication.CookieDomain; } _httpContext.Response.Cookies.Add(cookie); _cachedCustomer = customer; } public virtual void SignOut() { _cachedCustomer = null; FormsAuthentication.SignOut(); } public virtual Customer GetAuthenticatedCustomer() { if (_cachedCustomer != null) return _cachedCustomer; if (_httpContext == null || _httpContext.Request == null || !_httpContext.Request.IsAuthenticated || !(_httpContext.User.Identity is FormsIdentity)) { return null; } var formsIdentity = (FormsIdentity)_httpContext.User.Identity; var customer = GetAuthenticatedCustomerFromTicket(formsIdentity.Ticket); if (customer != null && customer.Active && !customer.Deleted && customer.IsRegistered()) _cachedCustomer = customer; return _cachedCustomer; } public virtual Customer GetAuthenticatedCustomerFromTicket(FormsAuthenticationTicket ticket) { if (ticket == null) throw new ArgumentNullException("ticket"); var usernameOrEmail = ticket.UserData; if (String.IsNullOrWhiteSpace(usernameOrEmail)) return null; var customer = _customerSettings.UsernamesEnabled ? _customerService.GetCustomerByUsername(usernameOrEmail) : _customerService.GetCustomerByEmail(usernameOrEmail); return customer; } }
FormsAuthenticationService類實現代碼分析
SignOut方法比較簡單,主要是調用了FormsAuthentication的退出方法。重點介紹SignIn()和GetAuthenticatedCustomer()方法
在SignIn()方法中,首先建立一個類型爲FormsAuthenticationTicket的ticket,而且將該ticket進行加密,而後將加密後的信息保存到cookie。
在GetAuthenticatedCustomer()中,若是說已經緩存了當前用戶,則直接返回,若是說當前http上下文沒有使用Forms驗證的話或者驗證不存在的話,則直接返回空置。緊接着,獲取以前已經保存的ticket,取到ticket以後,根據保存的UserData,獲取到已經登陸用戶的信息。
1 public enum CustomerLoginResults { /// <summary> /// Login successful /// </summary> Successful = 1, /// <summary> /// Customer dies not exist (email or username) /// </summary> CustomerNotExist = 2, /// <summary> /// Wrong password /// </summary> WrongPassword = 3, /// <summary> /// Account have not been activated /// </summary> NotActive = 4, /// <summary> /// Customer has been deleted /// </summary> Deleted = 5, /// <summary> /// Customer not registered /// </summary> NotRegistered = 6, }
1 public partial interface ICustomerRegistrationService { /// <summary> /// Validate customer /// </summary> /// <param name="usernameOrEmail">Username or email</param> /// <param name="password">Password</param> /// <returns>Result</returns> CustomerLoginResults ValidateCustomer(string usernameOrEmail, string password); }
1 public partial class CustomerRegistrationService : ICustomerRegistrationService { #region Fields private readonly ICustomerService _customerService; private readonly CustomerSettings _customerSettings; #endregion #region Ctor /// <summary> /// Ctor /// </summary> /// <param name="customerService">Customer service</param> /// <param name="encryptionService">Encryption service</param> /// <param name="newsLetterSubscriptionService">Newsletter subscription service</param> /// <param name="localizationService">Localization service</param> /// <param name="storeService">Store service</param> /// <param name="rewardPointsSettings">Reward points settings</param> /// <param name="customerSettings">Customer settings</param> public CustomerRegistrationService(ICustomerService customerService, CustomerSettings customerSettings) { this._customerService = customerService; this._customerSettings = customerSettings; } #endregion #region Methods /// <summary> /// Validate customer /// </summary> /// <param name="usernameOrEmail">Username or email</param> /// <param name="password">Password</param> /// <returns>Result</returns> public virtual CustomerLoginResults ValidateCustomer(string usernameOrEmail, string password) { Customer customer; if (_customerSettings.UsernamesEnabled) customer = _customerService.GetCustomerByUsername(usernameOrEmail); else customer = _customerService.GetCustomerByEmail(usernameOrEmail); if (customer == null) return CustomerLoginResults.CustomerNotExist; string pwd = ""; bool isValid = pwd == customer.Password; if (!isValid) return CustomerLoginResults.WrongPassword; //save last login date customer.LastLoginDateUtc = DateTime.UtcNow; _customerService.UpdateCustomer(customer); return CustomerLoginResults.Successful; } #endregion }
CustomerRegistrationService類實現代碼分析
在驗證用戶的方法中仍是比較簡單的,首先根據用戶用戶設置,是用戶名登陸仍是郵箱登陸,而後根據用戶名或者郵箱獲取到用戶信息,若是用戶信息爲空的話,則表示用戶不存在,不然,則更新登陸用戶的用戶信息。
nopcommerce項目中實現的用戶驗證絕非如此簡單,它提供了密碼加密格式的驗證,感興趣的同窗能夠下載看一看。
1 /// <summary> /// Work context /// </summary> public interface IWorkContext { /// <summary> /// Gets or sets the current customer /// </summary> Customer CurrentCustomer { get; set; } /// <summary> /// Get or set value indicating whether we're in admin area /// </summary> bool IsAdmin { get; set; } }
1 public partial class WebWorkContext : IWorkContext { #region Const private const string CustomerCookieName = "Nop.customer"; #endregion #region Fields private readonly HttpContextBase _httpContext; private readonly ICustomerService _customerService; private readonly IAuthenticationService _authenticationService; private Customer _cachedCustomer; #endregion #region Ctor public WebWorkContext(HttpContextBase httpContext, ICustomerService customerService, IAuthenticationService authenticationService,) { this._httpContext = httpContext; this._customerService = customerService; this._authenticationService = authenticationService; } #endregion #region Utilities protected virtual HttpCookie GetCustomerCookie() { if (_httpContext == null || _httpContext.Request == null) return null; return _httpContext.Request.Cookies[CustomerCookieName]; } protected virtual void SetCustomerCookie(Guid customerGuid) { if (_httpContext != null && _httpContext.Response != null) { var cookie = new HttpCookie(CustomerCookieName); cookie.HttpOnly = true; cookie.Value = customerGuid.ToString(); if (customerGuid == Guid.Empty) { cookie.Expires = DateTime.Now.AddMonths(-1); } else { int cookieExpires = 24*365; //TODO make configurable cookie.Expires = DateTime.Now.AddHours(cookieExpires); } _httpContext.Response.Cookies.Remove(CustomerCookieName); _httpContext.Response.Cookies.Add(cookie); } } #endregion #region Properties /// <summary> /// Gets or sets the current customer /// </summary> public virtual Customer CurrentCustomer { get { if (_cachedCustomer != null) return _cachedCustomer; Customer customer = null; //registered user if (customer == null || customer.Deleted || !customer.Active) { customer = _authenticationService.GetAuthenticatedCustomer(); } //impersonate user if required (currently used for 'phone order' support) if (customer != null && !customer.Deleted && customer.Active) { var impersonatedCustomerId = customer.GetAttribute<int?>(SystemCustomerAttributeNames.ImpersonatedCustomerId); if (impersonatedCustomerId.HasValue && impersonatedCustomerId.Value > 0) { var impersonatedCustomer = _customerService.GetCustomerById(impersonatedCustomerId.Value); if (impersonatedCustomer != null && !impersonatedCustomer.Deleted && impersonatedCustomer.Active) { //set impersonated customer _originalCustomerIfImpersonated = customer; customer = impersonatedCustomer; } } } //load guest customer if (customer == null || customer.Deleted || !customer.Active) { var customerCookie = GetCustomerCookie(); if (customerCookie != null && !String.IsNullOrEmpty(customerCookie.Value)) { Guid customerGuid; if (Guid.TryParse(customerCookie.Value, out customerGuid)) { var customerByCookie = _customerService.GetCustomerByGuid(customerGuid); if (customerByCookie != null && //this customer (from cookie) should not be registered !customerByCookie.IsRegistered()) customer = customerByCookie; } } } //create guest if not exists if (customer == null || customer.Deleted || !customer.Active) { customer = _customerService.InsertGuestCustomer(); } //validation if (!customer.Deleted && customer.Active) { SetCustomerCookie(customer.CustomerGuid); _cachedCustomer = customer; } return _cachedCustomer; } set { SetCustomerCookie(value.CustomerGuid); _cachedCustomer = value; } } /// <summary> /// Get or set value indicating whether we're in admin area /// </summary> public virtual bool IsAdmin { get; set; } #endregion }
WebWorkContext類實現代碼分析
該類的CurrentCustomer屬性的實現簡化了一部分,咱們首先判斷用戶信息是否已經緩存,若是已經緩存了,則直接返回,若是沒有,則須要從FormsAuthenticationService中從新獲取用戶信息,根據該用戶的信息,設置單個用戶的cookie。更加詳細的實現內容請下載nopcommerce源碼。
1 /// <summary> /// Authorize permission /// </summary> /// <param name="permission">Permission record</param> /// <returns>true - authorized; otherwise, false</returns> bool Authorize(PermissionRecord permission); /// <summary> /// Authorize permission /// </summary> /// <param name="permission">Permission record</param> /// <param name="customer">Customer</param> /// <returns>true - authorized; otherwise, false</returns> bool Authorize(PermissionRecord permission, Customer customer); /// <summary> /// Authorize permission /// </summary> /// <param name="permissionRecordSystemName">Permission record system name</param> /// <returns>true - authorized; otherwise, false</returns> bool Authorize(string permissionRecordSystemName); /// <summary> /// Authorize permission /// </summary> /// <param name="permissionRecordSystemName">Permission record system name</param> /// <param name="customer">Customer</param> /// <returns>true - authorized; otherwise, false</returns> bool Authorize(string permissionRecordSystemName, Customer customer);
1 public partial class PermissionService : IPermissionService { #region Constants /// <summary> /// Key pattern to clear cache /// </summary> private const string PERMISSIONS_PATTERN_KEY = "Nop.permission."; #endregion #region Fields private readonly IRepository<PermissionRecord> _permissionRecordRepository; private readonly ICustomerService _customerService; private readonly IWorkContext _workContext; #endregion #region Ctor /// <summary> /// Ctor /// </summary> /// <param name="permissionRecordRepository">Permission repository</param> /// <param name="customerService">Customer service</param> /// <param name="workContext">Work context</param> /// <param name="localizationService">Localization service</param> /// <param name="languageService">Language service</param> /// <param name="cacheManager">Cache manager</param> public PermissionService(IRepository<PermissionRecord> permissionRecordRepository, ICustomerService customerService, IWorkContext workContext) { this._permissionRecordRepository = permissionRecordRepository; this._customerService = customerService; this._workContext = workContext; } #endregion #region Methods /// <summary> /// Authorize permission /// </summary> /// <param name="permission">Permission record</param> /// <returns>true - authorized; otherwise, false</returns> public virtual bool Authorize(PermissionRecord permission) { return Authorize(permission, _workContext.CurrentCustomer); } /// <summary> /// Authorize permission /// </summary> /// <param name="permission">Permission record</param> /// <param name="customer">Customer</param> /// <returns>true - authorized; otherwise, false</returns> public virtual bool Authorize(PermissionRecord permission, Customer customer) { if (permission == null) return false; if (customer == null) return false; //old implementation of Authorize method //var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); //foreach (var role in customerRoles) // foreach (var permission1 in role.PermissionRecords) // if (permission1.SystemName.Equals(permission.SystemName, StringComparison.InvariantCultureIgnoreCase)) // return true; //return false; return Authorize(permission.SystemName, customer); } /// <summary> /// Authorize permission /// </summary> /// <param name="permissionRecordSystemName">Permission record system name</param> /// <returns>true - authorized; otherwise, false</returns> public virtual bool Authorize(string permissionRecordSystemName) { return Authorize(permissionRecordSystemName, _workContext.CurrentCustomer); } /// <summary> /// Authorize permission /// </summary> /// <param name="permissionRecordSystemName">Permission record system name</param> /// <param name="customer">Customer</param> /// <returns>true - authorized; otherwise, false</returns> public virtual bool Authorize(string permissionRecordSystemName, Customer customer) { if (String.IsNullOrEmpty(permissionRecordSystemName)) return false; var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); foreach (var role in customerRoles) if (Authorize(permissionRecordSystemName, role)) //yes, we have such permission return true; //no permission found return false; } #endregion #region Utilities /// <summary> /// Authorize permission /// </summary> /// <param name="permissionRecordSystemName">Permission record system name</param> /// <param name="customerRole">Customer role</param> /// <returns>true - authorized; otherwise, false</returns> protected virtual bool Authorize(string permissionRecordSystemName, CustomerRole customerRole) { if (String.IsNullOrEmpty(permissionRecordSystemName)) return false; string key = string.Format(PERMISSIONS_ALLOWED_KEY, customerRole.Id, permissionRecordSystemName); return _cacheManager.Get(key, () => { foreach (var permission1 in customerRole.PermissionRecords) if (permission1.SystemName.Equals(permissionRecordSystemName, StringComparison.InvariantCultureIgnoreCase)) return true; return false; }); } #endregion }
PermissionService類實現代碼分析
分析Authorize代碼能夠知道,首先是要獲取當前用戶所隸屬的全部用戶組,而後根據權限名稱和用戶組判斷是否具備某個模塊的訪問權限。
3.3 相關控制器源代碼
PermissionController類:主要是兩個登陸的Action,經過一下代碼能夠看出,當用戶登陸成功以後,須要調用 _authenticationService的登陸方法,經過該方法記錄已經登陸用戶的ID
1 [NopHttpsRequirement(SslRequirement.Yes)] public ActionResult Login(bool? checkoutAsGuest) { var model = new LoginModel(); model.UsernamesEnabled = _customerSettings.UsernamesEnabled; model.CheckoutAsGuest = checkoutAsGuest.GetValueOrDefault(); model.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage; return View(model); } [HttpPost] [CaptchaValidator] public ActionResult Login(LoginModel model, string returnUrl, bool captchaValid) { //validate CAPTCHA if (_captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage && !captchaValid) { ModelState.AddModelError("", _localizationService.GetResource("Common.WrongCaptcha")); } if (ModelState.IsValid) { if (_customerSettings.UsernamesEnabled && model.Username != null) { model.Username = model.Username.Trim(); } var loginResult = _customerRegistrationService.ValidateCustomer(_customerSettings.UsernamesEnabled ? model.Username : model.Email, model.Password); switch (loginResult) { case CustomerLoginResults.Successful: { var customer = _customerSettings.UsernamesEnabled ? _customerService.GetCustomerByUsername(model.Username) : _customerService.GetCustomerByEmail(model.Email); //migrate shopping cart _shoppingCartService.MigrateShoppingCart(_workContext.CurrentCustomer, customer, true); //sign in new customer _authenticationService.SignIn(customer, model.RememberMe); //activity log _customerActivityService.InsertActivity("PublicStore.Login", _localizationService.GetResource("ActivityLog.PublicStore.Login"), customer); if (String.IsNullOrEmpty(returnUrl) || !Url.IsLocalUrl(returnUrl)) return RedirectToRoute("HomePage"); return Redirect(returnUrl); } case CustomerLoginResults.CustomerNotExist: ModelState.AddModelError("", _localizationService.GetResource("Account.Login.WrongCredentials.CustomerNotExist")); break; case CustomerLoginResults.Deleted: ModelState.AddModelError("", _localizationService.GetResource("Account.Login.WrongCredentials.Deleted")); break; case CustomerLoginResults.NotActive: ModelState.AddModelError("", _localizationService.GetResource("Account.Login.WrongCredentials.NotActive")); break; case CustomerLoginResults.NotRegistered: ModelState.AddModelError("", _localizationService.GetResource("Account.Login.WrongCredentials.NotRegistered")); break; case CustomerLoginResults.WrongPassword: default: ModelState.AddModelError("", _localizationService.GetResource("Account.Login.WrongCredentials")); break; } } //If we got this far, something failed, redisplay form model.UsernamesEnabled = _customerSettings.UsernamesEnabled; model.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage; return View(model); }
SecurityController類:安全控制器,當用戶權限不足時,則展現AccessDenied界面。Permissions是訪問控制界面的Action
1 /// <summary>
2 /// 拒絕訪問 3 /// </summary>
4 /// <param name="pageUrl"></param>
5 /// <returns></returns>
6 public ActionResult AccessDenied(string pageUrl) 7 { 8 var currentCustomer = _workContext.CurrentCustomer; 9 if (currentCustomer == null || currentCustomer.IsGuest()) 10 { 11 _logger.Information(string.Format("Access denied to anonymous request on {0}", pageUrl)); 12 return View(); 13 } 14
15 _logger.Information(string.Format("Access denied to user #{0} '{1}' on {2}", currentCustomer.Email, currentCustomer.Email, pageUrl)); 16
17
18 return View(); 19 } 20
21 /// <summary>
22 /// GET:/Admin/Security/Permissions 23 /// 獲取權限列表 24 /// </summary>
25 /// <returns></returns>
26 public ActionResult Permissions() 27 { 28 if (!_permissionService.Authorize(StandardPermissionProvider.ManageAcl)) 29 return AccessDeniedView(); 30
31 var model = new PermissionMappingModel(); 32
33 var permissionRecords = _permissionService.GetAllPermissionRecords(); 34 var customerRoles = _customerService.GetAllCustomerRoles(true); 35 foreach (var pr in permissionRecords) 36 { 37 model.AvailablePermissions.Add(new PermissionRecordModel 38 { 39 //Name = pr.Name,
40 Name = pr.GetLocalizedPermissionName(_localizationService, _workContext), 41 SystemName = pr.SystemName 42 }); 43 } 44 foreach (var cr in customerRoles) 45 { 46 model.AvailableCustomerRoles.Add(new CustomerRoleModel 47 { 48 Id = cr.Id, 49 Name = cr.Name 50 }); 51 } 52 foreach (var pr in permissionRecords) 53 foreach (var cr in customerRoles) 54 { 55 bool allowed = pr.CustomerRoles.Count(x => x.Id == cr.Id) > 0; 56 if (!model.Allowed.ContainsKey(pr.SystemName)) 57 model.Allowed[pr.SystemName] = new Dictionary<int, bool>(); 58 model.Allowed[pr.SystemName][cr.Id] = allowed; 59 } 60
61 return View(model); 62 } 63
64 /// <summary>
65 /// GET:/Admin/Security/Permissions 66 /// 提交訪問權限 67 /// </summary>
68 /// <param name="form"></param>
69 /// <returns></returns>
70 [HttpPost, ActionName("Permissions")] 71 public ActionResult PermissionsSave(FormCollection form) 72 { 73 if (!_permissionService.Authorize(StandardPermissionProvider.ManageAcl)) 74 return AccessDeniedView(); 75
76 var permissionRecords = _permissionService.GetAllPermissionRecords(); 77 var customerRoles = _customerService.GetAllCustomerRoles(true); 78
79
80 foreach (var cr in customerRoles) 81 { 82 string formKey = "allow_" + cr.Id; 83 var permissionRecordSystemNamesToRestrict = form[formKey] != null ? form[formKey].Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() : new List<string>(); 84
85 foreach (var pr in permissionRecords) 86 { 87
88 bool allow = permissionRecordSystemNamesToRestrict.Contains(pr.SystemName); 89 if (allow) 90 { 91 if (pr.CustomerRoles.FirstOrDefault(x => x.Id == cr.Id) == null) 92 { 93 pr.CustomerRoles.Add(cr); 94 _permissionService.UpdatePermissionRecord(pr); 95 } 96 } 97 else
98 { 99 if (pr.CustomerRoles.FirstOrDefault(x => x.Id == cr.Id) != null) 100 { 101 pr.CustomerRoles.Remove(cr); 102 _permissionService.UpdatePermissionRecord(pr); 103 } 104 } 105 } 106 } 107
108 SuccessNotification(_localizationService.GetResource("Admin.Configuration.ACL.Updated")); 109 return RedirectToAction("Permissions"); 110 }
3.4 相關View源代碼
PermissionMappingModel:訪問控制管理界面模型
1 public partial class PermissionMappingModel : BaseNopModel { public PermissionMappingModel() { AvailablePermissions = new List<PermissionRecordModel>(); AvailableCustomerRoles = new List<CustomerRoleModel>(); Allowed = new Dictionary<string, IDictionary<int, bool>>(); } public IList<PermissionRecordModel> AvailablePermissions { get; set; } public IList<CustomerRoleModel> AvailableCustomerRoles { get; set; } //[permission system name] / [customer role id] / [allowed] public IDictionary<string, IDictionary<int, bool>> Allowed { get; set; } }
Permissions.cshtml:訪問控制管理界面
1 @model PermissionMappingModel 2 @{ //page title ViewBag.Title = T("Admin.Configuration.ACL").Text; 3 } 4 @using (Html.BeginForm()) 5 { @Html.AntiForgeryToken() <div class="section-header"> <div class="title"> <img src="@Url.Content("~/Administration/Content/images/ico-configuration.png")" alt="" /> @T("Admin.Configuration.ACL") </div> <div class="options"> <input type="submit" name="save" class="k-button" value="@T("Admin.Common.Save")" /> </div> </div> <table class="adminContent"> <tr> <td> @if (Model.AvailablePermissions.Count == 0) { <text>No permissions defined</text> } else if (Model.AvailableCustomerRoles.Count == 0) { <text>No customer roles available</text> } else { <script type="text/javascript"> $(document).ready(function () { @foreach (var cr in Model.AvailableCustomerRoles) { <text> $('#selectall-@(cr.Id)').click(function () { $('.allow_@(cr.Id)').attr('checked', $(this).is(':checked')).change(); }); </text> } }); </script> <table class="tablestyle" cellspacing="0" rules="all" border="1" style="width: 100%; border-collapse: collapse;"> <tbody> <tr class="headerstyle"> <th scope="col"> <strong>@T("Admin.Configuration.ACL.Permission")</strong> </th> @foreach (var cr in Model.AvailableCustomerRoles) { <th scope="col"> <strong>@cr.Name</strong> <input type="checkbox" id="selectall-@(cr.Id)" /> </th> } </tr> @{ bool altRow = true; } @foreach (var pr in Model.AvailablePermissions) { altRow = !altRow; <tr class="@(altRow ? "altrowstyle" : "rowstyle")"> <td> <span>@pr.Name</span> </td> @foreach (var cr in Model.AvailableCustomerRoles) { var allowed = Model.Allowed.ContainsKey(pr.SystemName) && Model.Allowed[pr.SystemName][cr.Id]; <td> <input class="allow_@(cr.Id)" class="allow_@(cr.Id)" type="checkbox" value="@(pr.SystemName)" name="allow_@(cr.Id)" @(allowed ? " checked=checked" : null) /> </td> } </tr> } </tbody> </table> } </td> </tr> </table>
6
7 }
4. 總結
經過對nopcommerce項目中的訪問控制代碼的分析,使得我對MVC4下的用戶驗證的實現有了進一步的瞭解:經過FormsAuthentication和HttpCookie來進行用戶驗證以及具體的實現過程。
該項目中的訪問控制也是值得學習的,使得權限記錄和用戶組發送多對多的關係,當能查詢到某個用戶組含有對應的訪問權限,則表示該用戶組對這個模塊有訪問權限。
經過學習nopcommerce項目代碼,我也編寫了一個小Demo:AclDemo,該項目將在下一篇進行分析和源代碼下載,敬請期待,感興趣的能夠關注我。