我一直在寻找使用最新身份声明功能在MVC中实现授权的方法(多年来使用Active Directory实现一切!)。我在下面发布了一条建议,但我的问题是,有没有人有更好的想法?还有更多"标准"实现这一目标的方法?
在当天,Forms Authentication Users和Roles是控制Web应用程序中的身份验证和授权的方式。通常使用与业务角色相对应的少量角色(例如,管理员,经理,工人)。有可能(现在仍然)以更高的粒度级别使用角色,比如使用字符串为每个MVC [控制器] + [动作]创建一个角色。这确实需要额外的表来以较低的粒度级别管理业务角色,但它可以非常简单地完成。
随着"声明"的出现在最新的ASP.NET身份中,我认为可以实现更好的授权实现。有很多方法可以做到这一点,我建议在下面给出一个相当小的方法。
答案 0 :(得分:0)
创建一个Controller基类并覆盖OnActionExecuting方法。我为数据库中的用户保存了一些声明,声明控制器名称的声明类型和声明值为"阅读","编辑","创建" ,"删除",这是用户在控制器上的典型声明。我已将这些声明添加到视图包中,以便在视图中使用。
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// get user claims
var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null)
{
// Get all user claims on this controller. In this controler base class, [this] still gets the descendant instance type, hence name
List<Claim> claims = user.Claims.Where(c => c.Type == this.GetType().Name).ToList();
// set Viewbag with default authorisations on this controller
ViewBag.ClaimRead = claims.Any(c => c.Value == "Read");
ViewBag.ClaimEdit = claims.Any(c => c.Value == "Edit");
ViewBag.ClaimCreate = claims.Any(c => c.Value == "Create");
ViewBag.ClaimDelete = claims.Any(c => c.Value == "Delete");
}
base.OnActionExecuting(filterContext);
}
控制器操作使用自定义授权来保护它们
[ClaimsAuthorize("ApplianceTypesController", "Create")]
public ActionResult Create()
{
return View();
}
在Razor视图中,仅在授权
时添加控件<p>
@if (ViewBag.ClaimCreate)
{
@Html.ActionLink("Create New", "Create")
}
</p>
我没有将额外的授权放入控制器和其他地方,而是将其放入自定义授权类
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
private string claimType;
private string claimValue;
public ClaimsAuthorizeAttribute(string type, string value = "")
{
this.ClaimType = type;
this.ClaimValue = value;
}
public string ClaimType { get => claimType; protected set => claimType = value; }
public string ClaimValue { get => claimValue; protected set => claimValue = value; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User != null)
{
var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null && user.HasClaim(ClaimType, ClaimValue))
{
filterContext.Result = null;
base.OnAuthorization(filterContext);
}
else
{
// we don't use 401 as this will cause a login loop : base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden, "You are forbidden to access this resource");
}
}
}
}
我没有使用标准的Site-Map,而是有一个基于Claim类型的菜单项表(忽略任何值)。我假设对控制器及其页面的任何声明都会显示菜单项以实现目标。这作为菜单
的一部分存在于局部视图中@foreach (var item in Model.Where(m => m.ParentId == null).OrderBy(m => m.SequenceNumber))
{
@Html.ActionLink(item.DisplayText, item.ActionName, item.ControllerName, null, new { @class = "nav-item nav-link active" })
}