以下是我的要求:
我将为N个角色添加用户;在数据库中定义。
我需要使用authorize属性保护每个控制器操作。
例如,Web应用程序将检查登录用户是否属于这两个角色中的任何一个,如果他们这样做,我允许他们进入。如何告诉Authorize属性从a获取用户角色我选择的数据库表?
[Authorize(Roles = "Admin, Technician")]
public ActionResult Edit(int id)
{
return View();
}
我已经尝试使用谷歌搜索许多不同的页面,但似乎没有一个符合我需要的东西,而且过于复杂。
如果official documentation有一些我也喜欢找到的东西,因为我没有看到任何可以使用的东西。
有什么建议吗?
例如,这个问题有一个非常干净的答案,但我不知道它是完整的还是缺少重要的东西。
ASP.NET MVC3 Role and Permission Management -> With Runtime Permission Assignment
修改
我真正想要的是创建一个自定义角色提供程序,对吗?我是否需要来实现此类并将其用作我的角色提供程序?我对此很新,有什么想法吗?
答案 0 :(得分:10)
过去几周我经历了几乎相同的情况,所以这可能会帮助其他人在同一条船上。我的场景是公司内部网上的MVC4应用程序,用户存储在Active Directory中。这允许Windows身份验证提供单点登录,因此不需要Forms身份验证。角色存储在Oracle数据库中。我有3个角色:
我决定使用asp.net角色提供程序api来创建自己的AccountRoleProvider。到目前为止,我只需要使用2个方法,GetRolesForUser和IsUserInRole:
public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
{
private readonly IAccountRepository _accountRepository;
public AccountRoleProvider(IAccountRepository accountRepository)
{
this._accountRepository = accountRepository;
}
public AccountRoleProvider() : this (new AccountRepository())
{}
public override string[] GetRolesForUser(string user521)
{
var userRoles = this._accountRepository.GetRoles(user521).ToArray();
return userRoles;
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = this.GetRolesForUser(username);
return Utils.IndexOfString(userRoles, roleName) >= 0;
}
}
我更新了web.config以使用我的角色提供程序:
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AccountRoleProvider">
<providers>
<clear/>
<add name="AccountRoleProvider"
type="MyApp.Infrastructure.AccountRoleProvider" />
</providers>
</roleManager>
然后我从AuthorizeAttribute,ReadOnlyAuthorize和CustomAuthorize创建了2个自定义属性。
ReadonlyAuthorize:
public class ReadonlyAuthorize : AuthorizeAttribute
{
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
if (!roles.Contains("readonly"))
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
}
}
CustomAuthorize:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string RedirectActionName { get; set; }
public string RedirectControllerName { get; set; }
private IAccountRepository _accountRepository;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
this._accountRepository = new AccountRepository();
var accessAllowed = false;
// Get the roles passed in with the (Roles = "...") on the attribute
var allowedRoles = this.Roles.Split(',');
if (!user.Identity.IsAuthenticated)
{
return false;
}
// Get roles for current user
var roles = this._accountRepository.GetRoles(user.Identity.Name);
foreach (var allowedRole in allowedRoles)
{
if (roles.Contains(allowedRole))
{
accessAllowed = true;
}
}
if (!accessAllowed)
{
return false;
}
return base.AuthorizeCore(httpContext);
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
{
var values = new RouteValueDictionary(new
{
action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
});
filterContext.Result = new RedirectToRouteResult(values);
}
}
}
两个不同属性的原因是我使用一个用于所有用户必须成为其成员的Readonly角色才能访问该应用。我可以在Global.asax的RegisterGlobalFilters方法中添加它,这意味着它会自动应用到每个Controller:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new ReadonlyAuthorize());
}
然后在CustomAuthorize中,我可以采用更细粒度的方法,并指定我想要的角色并应用于Controller或单个Action,例如下面我可以限制管理员角色中对用户的删除方法:
[AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
public ActionResult Delete(int id = 0)
{
var batch = myDBContext.Batches.Find(id);
if (batch == null)
{
return HttpNotFound();
}
return View(batch);
}
我需要采取进一步的步骤,例如使用当前用户所属的角色更新User对象。这将检索用户的角色,而不是每次都在我的自定义属性中,并且还使用User.IsInRole。在Gloal.asax中的Application_AuthenticateRequest中应该可以实现这样的东西:
var roles = "get roles for this user from respository";
if (Context.User != null)
Context.User = new GenericPrincipal(Context.User.Identity, roles);
答案 1 :(得分:1)
有很多方法可以解决这个问题。 Darin的方法和blowdarts(非常熟练的人 - 其中一个也是安全作者)在你提供的链接中都很不错。
要注意的一件事是缓存。如果使用服务器端outputcache缓存,则可能会无意中为一个返回给另一个用户的用户缓存某些内容。请参阅:
OutputCache and Authorize filters in MVC3
和
Why can't I combine [Authorize] and [OutputCache] attributes when using Azure cache (.NET MVC3 app)?
和
MVC Custom Authentication, Authorization, and Roles Implementation
有关其的更多信息以及如果使用authorize属性如何处理缓存。
答案 2 :(得分:0)
您可以使用默认的成员资格提供程序和角色提供程序,而不是实现自己的,但您还需要使用默认的asp.net成员资格数据库aspnetdb.mdf。
请参阅here了解演练