gpt4 book ai didi

ASP.NET MVC - 角色提供程序的替代方案?

转载 作者:行者123 更新时间:2023-12-03 01:39:30 25 4
gpt4 key购买 nike

我试图避免使用角色提供程序和成员资格提供程序,因为在我看来它太笨拙了,因此我试图制作自己的“版本”,它不那么笨拙并且更易于管理/灵活。现在我的问题是……除了角色提供者之外,还有其他不错的选择吗? (我知道我可以自定义角色提供者、成员(member)提供者等。)

通过更易于管理/灵活,我的意思是我仅限于使用 Roles 静态类,而不是直接实现到与数据库上下文交互的服务层中,相反,我必须使用 Roles 静态类,它有它的自己的数据库上下文等,表名也很糟糕..

提前致谢。

最佳答案

我和你处境相同 - 我一直讨厌 RoleProviders。是的,如果您想启动并运行一个小型网站,它们非常棒,但它们不太现实。我一直发现的主要缺点是它们将您直接与 ASP.NET 联系在一起。

我最近的一个项目的方式是定义几个属于服务层一部分的接口(interface)(注意:我对它们进行了相当多的简化 - 但您可以轻松地添加到它们中):

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;
}
}
}

然后您可以在 Controller 操作上进行装饰:

[RequirePermission(Roles.Accounting)]
public ViewResult Index()
{
// ...
}

这种方法的优点是您还可以使用依赖项注入(inject)和 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);
}

然后在您的 View 中(此处使用 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.
}
}

这样,我们就不会公开当前用户 session 中的密码哈希和所有其他详细信息,因为用户的 session 生命周期确实不需要它们。

关于ASP.NET MVC - 角色提供程序的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4837103/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com