我和你处境一样 - 我一直讨厌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
}
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
{
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);
var userSession = (User)filterContext.HttpContext.Session["CurrentUser"];
var success = authSvc.Authorize(userSession, this.permissions);
if (success)
{
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)
{
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()
{
}
}
通过这种方式,我们不会在当前用户的会话中暴露密码散列和其他细节,因为它们实际上对于用户会话的生命周期并不是必要的。