ASP.NET MVC - 角色提供者的替代品?
我試圖避免使用 Role Provider 和 Membership Provider,因為在我看來它太笨拙了,因此我試圖製作我自己的“版本”,它不那麼笨拙,更易於管理/靈活。現在是我的問題.. 有沒有一個不錯的角色提供者的替代品?(我知道我可以做自定義角色提供者、會員提供者等)
通過更易於管理/更靈活,我的意思是我僅限於使用 Roles 靜態類,而不是直接在與數據庫上下文互動的服務層中實現,而是我必須使用具有自己的數據庫上下文的 Roles 靜態類等等,表名也很糟糕..
提前致謝。
我和你在同一條船上 - 我一直討厭 RoleProviders。是的,如果您想為一個小型網站啟動並執行它們,它們很棒,但它們不是很現實。我一直發現的主要缺點是它們將您直接綁定到 ASP.NET。
我在最近的一個項目中採用的方法是定義幾個介面,它們是服務層的一部分(注意:我對它們進行了相當多的簡化——但你可以輕鬆地添加它們):
public interface IAuthenticationService { bool Login(string username, string password); void Logout(User user); } public interface IAuthorizationService { bool Authorize(User user, Roles requiredRoles); }然後你的使用者可以有一個
Roles枚舉:public enum Roles { Accounting = 1, Scheduling = 2, Prescriptions = 4 // What ever else you need to define here. // Notice all powers of 2 so we can OR them to combine role permissions. } public class User { bool IsAdministrator { get; set; } Roles Permissions { get; set; } }對於您的
IAuthenticationService,您可以有一個執行標準密碼檢查的基本實現,然後您可以有一個FormsAuthenticationService做更多的事情,例如設置 cookie 等。對於您的AuthorizationService,您需要這樣的東西:public class AuthorizationService : IAuthorizationService { public bool Authorize(User userSession, Roles requiredRoles) { if (userSession.IsAdministrator) { return true; } else { // Check if the roles enum has the specific role bit set. return (requiredRoles & user.Roles) == requiredRoles; } } }在這些基礎服務之上,您可以輕鬆添加服務來重置密碼等。
由於您使用的是 MVC,因此您可以使用以下命令在操作級別進行授權
ActionFilter:public class RequirePermissionFilter : IAuthorizationFilter { private readonly IAuthorizationService authorizationService; private readonly Roles permissions; public RequirePermissionFilter(IAuthorizationService authorizationService, Roles requiredRoles) { this.authorizationService = authorizationService; this.permissions = requiredRoles; this.isAdministrator = isAdministrator; } private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext) { return this.authorizationService ?? new FormsAuthorizationService(httpContext); } public void OnAuthorization(AuthorizationContext filterContext) { var authSvc = this.CreateAuthorizationService(filterContext.HttpContext); // Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService. var userSession = (User)filterContext.HttpContext.Session["CurrentUser"]; var success = authSvc.Authorize(userSession, this.permissions); if (success) { // Since authorization is performed at the action level, the authorization code runs // after the output caching module. In the worst case this could allow an authorized user // to cause the page to be cached, then an unauthorized user would later be served the // cached page. We work around this by telling proxies not to cache the sensitive page, // then we hook our custom authorization code into the caching mechanism so that we have // the final say on whether or not a page should be served from the cache. var cache = filterContext.HttpContext.Response.Cache; cache.SetProxyMaxAge(new TimeSpan(0)); cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) => { validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context)); }, null); } else { this.HandleUnauthorizedRequest(filterContext); } } private void HandleUnauthorizedRequest(AuthorizationContext filterContext) { // Ajax requests will return status code 500 because we don't want to return the result of the // redirect to the login page. if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) { filterContext.Result = new HttpStatusCodeResult(500); } else { filterContext.Result = new HttpUnauthorizedResult(); } } public HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) { var authSvc = this.CreateAuthorizationService(httpContext); var userSession = (User)httpContext.Session["CurrentUser"]; var success = authSvc.Authorize(userSession, this.permissions); if (success) { return HttpValidationStatus.Valid; } else { return HttpValidationStatus.IgnoreThisRequest; } } }然後您可以在控制器操作上進行裝飾:
[RequirePermission(Roles.Accounting)] public ViewResult Index() { // ... }這種方法的優點是您還可以使用依賴注入和 IoC 容器來連接。此外,您可以在多個應用程序(不僅僅是您的 ASP.NET 應用程序)中使用它。您將使用您的 ORM 來定義適當的模式。
如果您需要有關
FormsAuthorization/Authentication服務的更多詳細資訊或從這裡去哪裡,請告訴我。編輯:要添加“安全修剪”,您可以使用 HtmlHelper 來完成。這可能需要多一點……但你明白了。
public static bool SecurityTrim<TModel>(this HtmlHelper<TModel> source, Roles requiredRoles) { var authorizationService = new FormsAuthorizationService(); var user = (User)HttpContext.Current.Session["CurrentUser"]; return authorizationService.Authorize(user, requiredRoles); }然後在您的視圖中(在此處使用 Razor 語法):
@if(Html.SecurityTrim(Roles.Accounting)) { <span>Only for accounting</span> }編輯:
UserSession看起來像這樣:public class UserSession { public int UserId { get; set; } public string UserName { get; set; } public bool IsAdministrator { get; set; } public Roles GetRoles() { // make the call to the database or whatever here. // or just turn this into a property. } }這樣,我們不會在目前使用者的會話中公開密碼雜湊和所有其他詳細資訊,因為使用者的會話生命週期實際上不需要它們。