我正在尝试在ASP.NET Core中创建自定义授权属性。 在以前的版本中,可以覆盖bool AuthorizeCore(HttpContextBase httpContext)。 但是AuthorizeAttribute中不再存在这种情况。
制作自定义AuthorizeAttribute的当前方法是什么?
我想要完成的任务:我在Header Authorization中收到一个会话ID。 从该ID我将知道特定动作是否有效。
-
我不知道该怎么做,但MVC是开源的。 您可以拉出github仓库并查找IAuthorizationFilter的实现。 如果我今天有时间,我会找你并发表实际答案,但没有承诺。 github repo:github.com/aspnet/Mvc
-
好的,没时间了,但是在aspnet / Security repo中使用AuthorizeAttribute的MVC Repo中寻找AuthorizationPolicy的用法,请访问:github.com/aspnet/Security。 或者,查看MVC repo中的命名空间,其中您关注的安全性内容似乎存在,即Microsoft.AspNet.Authorization。 对不起,我不能提供更多帮助。 祝好运!
ASP.Net核心团队建议的方法是使用新的策略设计,此处已完整记录。新方法背后的基本思想是使用新的[Authorize]属性来指定"策略"(例如[Authorize( Policy ="YouNeedToBe18ToDoThis")],其中策略在应用程序的Startup.cs中注册以执行某些代码块(即确保用户具有年龄在18岁或以上的年龄申请表。
策略设计是框架的一个很好的补充,ASP.Net安全核心团队的引入应该受到赞扬。也就是说,它并不适合所有情况。这种方法的缺点在于它无法为最简单地断言给定控制器或动作需要给定声明类型的最常见需求提供方便的解决方案。如果应用程序可能具有数百个离散权限来管理各个REST资源上的CRUD操作("CanCreateOrder","CanReadOrder","CanUpdateOrder","CanDeleteOrder"等),则新方法要么重复一次到一次 - 策略名称和声明名称之间的一个映射(例如options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission,"CanUpdateOrder));),或编写一些代码以在运行时执行这些注册(例如,从数据库中读取所有声明类型并在循环中执行上述调用)。对于大多数情况,这种方法的问题在于它是不必要的开销。
虽然ASP.Net核心安全团队建议永远不要创建自己的解决方案,但在某些情况下,这可能是最谨慎的选择。
以下是使用IAuthorizationFilter提供表达给定控制器或操作的声明要求的简单方法的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute (string claimType, string claimValue ) : base(typeof(ClaimRequirementFilter ))
{
Arguments = new object[] {new Claim (claimType, claimValue ) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim ;
public ClaimRequirementFilter (Claim claim )
{
_claim = claim ;
}
public void OnAuthorization (AuthorizationFilterContext context )
{
var hasClaim = context .HttpContext.User.Claims.Any(c => c .Type == _claim .Type && c .Value == _claim .Value);
if (!hasClaim )
{
context .Result = new ForbidResult ();
}
}
}
[Route ("api/resource")]
public class MyController : Controller
{
[ClaimRequirement (MyClaimTypes .Permission, "CanReadResource")]
[HttpGet ]
public IActionResult GetResource ()
{
return Ok ();
}
} |
-
这应该标记为正确的答案。在这里,您可以看到Microsoft的人员如何看待开发人员的反馈。我不明白他们是如此"封闭"的原因,因为这是一个非常普遍的情况,拥有不同权限的镜像,必须为每一个编写一个策略是完全矫枉过正。我这么长时间都在寻找这个...(我差不多两年前已经问过这个问题,当时vNext仍然在这里赌注:stackoverflow.com/questions/32181400/…但我们仍然坚持在那里)
-
这是件好事。我们在Web API上有身份验证中间件,但是按角色分配了授权权限的安全性;所以不得不抛出一个属性,如:[MyAuthorize(MyClaimTypes.Permission,MyClaimValueTypes.Write,MyPermission.Employee)]看起来很好。
-
这是一个很好的方法。我根据我的情况对此进行了修改,以处理Permissions(带有flags属性的枚举)。
-
@Derek Greer:这是最好的答案。但是,您实现的是在授权操作筛选器之后运行的ActionFilter。反正有没有实现和授权动作过滤器?
-
@JacobPhan你是对的,使用IAuthorizationFilter接口可以更好地实现。我已更新代码以反映更改。
-
所以new ForbidResult()不起作用(导致异常/ 500),因为它没有相关的授权方案。这个案子我会用什么?
-
另请参阅此github问题中的讨论:github.com/aspnet/Mvc/issues/5607,该问题因此问题而关闭:github.com/aspnet/Security/issues/1359
-
您好Dereck,有什么办法可以使用ClaimRequirementAttribute和params string []值列表吗?类似的东西:public ClaimRequirementAttribute(string claimType, params string[] claimValues)
-
当然,只需更改属性和过滤器的参数类型即可。
-
有没有办法在WebApi 2中使用这样的功能?
-
创建一个新问题并将其链接到此处,我将提供一个示例。
-
@DerekGreer你能提供一个Github测试项目,因为在我的解决方案中,ClaimRequirementFilter的构造函数被调用两次。 Claim对象(参数)属性在第一次调用期间为null,在第二次调用时为空。 OnAuthorization仅使用空值调用一次
-
github.com/derekgreer/authorize-example
-
如何将类实例(如提供者或存储库)注入ClaimRequirementFilter?
-
@ Alex75使用context.HttpContext.RequestServices,你无法真正注入那里。
-
有没有办法在c# WebApi中使用?
-
@yogendarji如果您尝试在旧版本中执行此操作,那么您只需扩展AuthorizeAttribute。这个答案是关于如何在.Net Core中使用旧版本中的功能。
-
@yogendarji这里有一个经典的WebApi示例
-
请注意,如果您的OnAuthorization实现需要等待异步方法,则应该实现IAsyncAuthorizationFilter而不是IAuthorizationFilter,否则您的过滤器将同步执行,并且无论过滤器的结果如何,您的控制器操作都将执行。
-
@Codemunkie如果正在实现的行为需要使用异步方法,那么实现接口的异步版本肯定是可取的,但是您的注释实际上可以被提炼为不调用其结果或副作用的异步行为的建议需要在其他操作之前先完成。
-
这是一个很棒的答案!但是,它不适用于SignalR Hub和Hub方法
我是asp.net安全人员。 首先让我道歉,在音乐商店样本或单元测试之外,这些都没有被记录下来,并且它们仍然在暴露的API方面进行了改进。 s>详细的文档在这里。
我们不希望您编写自定义授权属性。如果你需要这样做我们做错了什么。相反,你应该写授权要求。
授权作用于身份。身份通过身份验证创建。
您在评论中说要检查标题中的会话ID。您的会话ID将成为身份的基础。如果您想使用Authorize属性,您需要编写一个身份验证中间件来获取该标头并将其转换为经过身份验证的ClaimsPrincipal。然后,您将在授权要求中检查该内容。授权要求可以像您一样复杂,例如,这是一个对当前身份提出出生日期索赔的要求,并且如果用户超过18岁则会授权;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
{
context.Fail();
return;
}
var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
int age = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-age))
{
age--;
}
if (age >= 18)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
} |
然后在你的ConfigureServices()功能中,你将它连接起来
1 2 3 4 5
| services .AddAuthorization(options =>
{
options .AddPolicy("Over18",
policy => policy .Requirements.Add(new Authorization .Over18Requirement()));
}); |
最后将其应用于控制器或操作方法
1
| [Authorize(Policy ="Over18")] |
-
感谢您制作AuthorizationHandler的示例;我能够让这个工作。或者,似乎我可以通过继承ActionFilterAttribute并覆盖OnActionExecuting来完成我需要的东西,尽管这将是"hacky",因为它在身份验证框架之外。
-
是的,请不要低头行动过滤器的路线。我们希望新的基于策略的系统应该足够灵活,适用于99%的场景,并且作为奖励,也可以在第三方框架中使用。如果您发现案例不适合您,请通过MSFT给我发电子邮件。
-
@blowdart是在没有策略值的情况下处理授权的方法"options.AddPolicy(string.Empty,policy => policy.AddRequirements(new Test()));" ?我正在尝试它,但现在我得到一个空页+调试不起作用
-
@ AndreasM&#252; ller我很有意思你为什么要一个空白的政策名称以及你期望发生的事情。这不是我们测试或编码的东西。这应该是github上的一个问题。
-
@blowdart好。基本上我想手动控制如何验证授权,即使它只是[授权]没有任何政策。这样就可以很容易地允许完全独立于所设置的ClaimsPrincipal的场景(例如,想象一次使用请求令牌)。你打算提出这个问题吗?我可以尝试,但上次我有理由这样做,我迷失在githubs奇怪的用户界面,并放弃了
-
这仍然无法解释为什么你想要一个空白的政策名称。在任何情况下,你仍然需要一个校长。这就是授权的作用。由于我仍然不理解你的问题,我宁愿你打开它。 github.com/aspnet/security/issues并单击新问题按钮
-
@blowdart好吧。我回家后会这样做。谢谢。
-
@blowdart如何在此要求中访问HttpContext / RouteData(params)?似乎AuthorizationContext context.Resource是有限的。谢谢!
-
@daredev你可以在AuthenticationHandler中找到他们。请参阅stackoverflow.com/a/31688792/571637
-
@dareDev你也可以将context.Resouce转换为Microsoft.AspNet.Mvc.AuthorizationContext。示例:Microsoft.AspNet.Mvc.AuthorizationContext resource =(Microsoft.AspNet.Mvc.AuthorizationContext)context.Resource; string id = resource.RouteData.Values ["id"]。ToString();
-
我想知道......如何实现细粒度的访问控制呢?让我们说一下Music Store示例中的ManageStore要求。正如样本中所示,只有"允许全部或全部"方式才能实现。那么我们是否必须为每种可能的排列创建一个新策略?即"用户/阅读","用户/创建","用户/指定角色","用户/删除",如果我们想要细粒度的声明?听起来很像设置工作以使其工作和丰富的策略只是为了管理声明而不是[ClaimsAutzorization("User","Read","Create","Delete","Assign")]属性?
-
我们重写了它,因为编写自己的authorize属性充满了失败并且被打破了。
-
现在,最后,记录。 docs.asp.net/en/latest/security/authorization/index.html
-
我必须评论说,所有这些都比实现自定义授权方法更复杂。我知道我希望如何完成授权我可以在MVC 5中编写它,在MVC 6中它们添加了许多"完成"代码,实际上比实现核心"事物"本身更复杂。让我坐在一个页面前试图找出一些东西,而不是直接编写代码,对于使用除Microsoft(或No-Sql)以外的RDBMS的人来说也是一个巨大的痛苦。
-
我纠正了自己,所有的请求都可以使用Microsoft.AspNet.Mvc.Filters和Middleware类进行精细过滤和处理,AspNet 5确实提供了内置的复杂工具,并且可以从请求处理的根本处调整Web应用程序。一个非常好的和复杂的管道系统。
-
@blowdart政策是否有任何命名惯例?
-
不,他们是字符串,你可以随意命名。
-
从我的角度来看,这并不能解决所有情况。在MVC 6之前,我使用了自定义授权属性来实现我自己的"权限系统"。我可以将Authorize属性添加到所有操作,并传递一个特定的必需权限(作为枚举值)。权限本身已映射到数据库中的组/用户。所以,我没有办法用政策处理这个问题!?
-
您将使用策略和要求替换您的枚举,然后将您的数据库替换为处理程序。
-
我同意,使用Authorization属性似乎不可能,因为一个人无法访问属性中的权限值。 @blowdart:不认为它会起作用,策略和处理程序无法访问属性中编码的权限值。
-
现在这已经失去控制。如果您有疑问,请将其创建为问题,而不是评论。
-
@blowdart:不是问题。
-
"我们不希望你编写自定义授权属性。如果你需要这样做,我们就做错了。"当用户通过身份验证但未获得授权时,如何返回403 Forbidden响应? 401用户通过身份验证但未授权是错误的。
-
@danludwig这就是它的作用。你试过吗?你还有别的什么吗?如果是这样,请记录错误。
-
blowdart:如果针对每个特定操作的"Over18"要求是任意年龄,您将如何编写此AuthorizationHandler?我是否必须为每个可能的年龄编写一份注册要求? @Gerwal,Tseng和我的情况就是如此。如何将参数传递给属性的具体"实例"???
-
@ Vi100你要参数化它。见github.com/blowdart/AspNetAuthorization-Samples/blob/master/src/…
-
这才是重点。您可以参数化需求,但不能参数处理程序,也不能参数...您不能这样做:[授权(MinAge = 21)]当然年龄是一个例子,我需要传递一个超过一百个枚举的枚举处理程序的值。这样我就不得不为每个可能的需求值编写一个处理程序("Over18","Over19","Over20"等)。
-
您可以参数化该要求,然后在配置策略时对其进行配置。所以你有多个策略,但只有一个处理程序和一个需求。来RC2您将能够替换策略提供者,因此您可以将您的需求字符串化为[Authorize(Policy ="Over18")]然后解析策略名称本身并返回您喜欢的任何策略。
-
我和这些评论中的许多其他人一样,非常失望的是,使用属性进行授权已经在Web API 2中被大大扼杀了。抱歉,但是你的"需求"抽象无法涵盖我们以前可以使用的任何情况属性构造函数参数,用于通知基础授权算法。它过去很简单,只做[CustomAuthorize(Operator.And, Permission.GetUser, Permission.ModifyUser)]之类的事情。我可以通过修改构造函数参数以无限多种方式使用单个自定义属性。
-
令我震惊的是,自称"领导ASP.NET安全人员"实际上建议使用魔术字符串(黑客的IAuthorizeData.Policy的意思)和自定义政策提供者来克服这种明显的疏忽,而不是在框架内解决它。我以为我们不应该创建自己的实现?你让我们中的几个人别无选择,只能从头开始重新实现授权(再次),这次甚至没有Web API旧的Authorize属性的好处。现在我们必须在动作过滤器或中间件级别上执行此操作。
-
我在github.com/aspnet/Mvc/issues/5532上发布了一个GitHub问题,它不仅总结了这个问题,还总结了我在尝试从Web API 2迁移到MVC 6时注意到的其他几个糟糕的设计选择。
-
@NathanAldenSr请参阅我的回答,它将允许您在控制器和操作上使用自定义属性,并在AuthorizationHandler中访问它们。 stackoverflow.com/a/40824351/436494
-
@ Vi100也看到了我的答案。 stackoverflow.com/a/40824351/436494
-
@Shawn这是一个相当过度的工程......我使用一个接收参数的简单AuthorizationFilterAttribute解决了这个问题。你不需要对此进行反思,它似乎比"官方"解决方案(我觉得很差)更具有人工性。
-
@blowdart有没有办法在需求中访问HTTP头?
-
@SHM作为一个单独的问题会更好
-
我已经对这个问题产生的问题添加了评论:github.com/aspnet/Mvc/issues/5607#issuecomment-269266125
-
对不起,如果我们必须编写我们需要的东西,你会觉得你会做错事。我真的需要这个功能,它被不必要地带走了。你不需要为我们制作程序,我们是程序员。
-
我有兴趣听到更多关于属性方法如何被破坏以及它们的使用会产生什么问题,因为你建议不要让任何人使用这种方法。
-
我需要一个自定义authorize属性来测试我的应用程序与TestServer ..
-
@blowdart感谢您的解释如果此政策失败,如何重定向到不同的视图或路线
-
虽然这是asp.net核心团队的选择,但我认为告诉开发人员他们无法处理自己的实现并不是他们的事。
-
好的......你做错了什么!
-
老实说,我非常喜欢.Net Core!但我发现内置的身份验证框架(Identity)不适合我,它需要一个严格的应用程序和数据库结构。
-
政策是不够的。如果您需要检查声明以查看某个用户是否是某个组或组织的成员,但该组或组织可能因请求而异,那么政策就会完全失效。这个新系统几乎没有价值。
-
我不能让这个评论来自Jeremy,特别是在政策评估期间提供请求时,所以政策根本不会落在这里,它完全支持给出关于它们为什么不起作用的例子。
-
@blowdart是否有一些AuthorizationHandler的单元测试示例?具体来说,如果/当用户使用context.User.IsInRole(roleName)属于AD组时,我正在尝试测试我的处理程序是否正常运行
-
@blowdart ASP.Net是否存在类似的文档,或者仅限Core?
-
@Felype你听起来真的很适应。我也在你的位置,我已经读到了我在asp.net mvc 5上拥有相同的aspnet核心安全编码。技术将永远进步,我们应该始终保持甚至前进尽我们所能,或者我们会落后。爱!
-
@EdgarSalazar是的,我想,我从早期的测试版开始就使用.net核心,现在我知道我正在使用的故事,它基于Ruby的Rack / Sinatra,具有非常强大的Nodejs影响力,我研究了两个那些表面上看,它确实增加了很多,我也在SPA和Rest学到了很多东西,并且也在调试JavaScript开发流程。
-
我有兴趣听到关于属性方法如何被破坏的更多信息 - @DerekGreer:我知道这是一个老问题,但我相信你可以在被动属性中找到你的答案。使属性包含行为不是一种好的设计方法,因为它们具有约束构造函数,这使得无法使用DI模式注入依赖项。将行为分解为可以在启动时注册(并由被动属性控制)的单个组件是一种更好的方法。
-
您不能对属性使用构造函数注入,但您可以执行属性注入。使用Autofac实际上相当容易,我想对其他主要容器进行映像支持。除此之外,我非常怀疑当他说设计被打破时,他指的是设计的方面。
-
我在我的代码中使用了这种方法,但我在这里看到的最大问题是使用Authorize属性,因为您需要为策略名称键入一个字符串。如果我使用策略名称出错,则没有编译器支持/类型检查。我希望能够传入枚举,但又要求自定义授权属性。不确定答案在这里。
-
您好Over18Requirement只是一个类,您可以共享代码吗?
-
@ Dave3of5,你可以使用nameof(SomeEnum.SomeValue)。这是一个黑客,但它的工作原理。
-
哦,到底是什么,伙计......我在这个非常紧迫的截止日期,我没有时间或精力学习如何设计中间件或声明或其中任何一个......框架的要点是抽象这些东西对我们来说因此我们可以只关注核心业务逻辑(在我的情况下,使用Firebase Admin SDK执行一些代码以确保持有者令牌有效)。什么应该是3行代码变成了更多的东西。
看来,使用ASP.NET Core 2,您可以再次继承AuthorizeAttribute,您只需要实现IAuthorizationFilter(或IAsyncAuthorizationFilter):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| [AttributeUsage (AttributeTargets .Class | AttributeTargets .Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _someFilterParameter ;
public CustomAuthorizeAttribute (string someFilterParameter )
{
_someFilterParameter = someFilterParameter ;
}
public void OnAuthorization (AuthorizationFilterContext context )
{
var user = context .HttpContext.User;
if (!user .Identity.IsAuthenticated)
{
// it isn't needed to set unauthorized result
// as the base class already requires the user to be authenticated
// this also makes redirect to a login page work properly
// context.Result = new UnauthorizedResult();
return;
}
// you can also use registered services
var someService = context .HttpContext.RequestServices.GetService<ISomeService >();
var isAuthorized = someService .IsUserAuthorized(user .Identity.Name, _someFilterParameter );
if (!isAuthorized )
{
context .Result = new StatusCodeResult ((int)System.Net.HttpStatusCode.Forbidden);
return;
}
}
} |
-
所以你只能用它来拒绝授权,而不是授予它?
-
@MEMark通过授予,您的意思是重写另一个授权属性?
-
我想你可以这样说。我的意思是有效地允许访问资源。
-
AFAIK,默认情况下允许访问,因此您需要明确拒绝它(例如,通过添加AuthorizeAttribute)。有关更多详细信息,请查看此问题:stackoverflow.com/questions/17272422/…
-
另请注意,在建议的示例中,不必从AuthorizeAttribute继承。您可以继承Attribute和IAuthorizationFilter。这样,如果使用某些非标准身份验证机制,则不会出现以下异常:InvalidOperationException:未指定authenticationScheme,并且未找到DefaultChallengeScheme。
-
@Anatolyevich如果我选择context.Result = new ForbidResult();,我仍会收到该错误。但到目前为止我还是喜欢这个选项。
-
请注意,如果您的OnAuthorization实现需要等待异步方法,则应该实现IAsyncAuthorizationFilter而不是IAuthorizationFilter,否则您的过滤器将同步执行,并且无论过滤器的结果如何,您的控制器操作都将执行。
What is the current approach to make a custom AuthorizeAttribute
简单:不要创建自己的AuthorizeAttribute。
对于纯授权方案(如仅限制对特定用户的访问),建议的方法是使用新的授权块:https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84 -L92
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AuthorizationOptions>(options =>
{
options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action","ManageStore"));
});
}
}
public class StoreController : Controller
{
[Authorize(Policy ="ManageStore"), HttpGet]
public async Task<IActionResult> Manage() { ... }
} |
对于身份验证,最好在中间件级别处理。
你想要完成什么?
-
我在Header Authorization中收到会话ID。从该ID我将知道特定动作是否有效。
-
那不是授权问题。我猜你的"会话ID"实际上是一个包含调用者身份的令牌:这绝对应该在中间件级别完成。
-
它不是身份验证(确定用户是谁),而是授权(确定用户是否应该访问资源)。那么你建议我在哪里解决这个问题?
-
好奇:你是如何创建会话标识符的?
-
@jltrem,同意,你所谈论的是授权,而不是身份验证。
-
@Pinpoint我不是在创造它们。我只是中间人提供访问另一个系统的人。
-
@EricBurcham会话标识符绝对是一种识别(=验证)用户的方式,我担心。
-
@jltrem但你从标识符中提取一些信息以确定谁是调用者,对吧?
-
@Pinpoint我不是。我查询该信息的另一个系统。该系统验证(确定用户)并授权(告诉我该用户可以访问的内容)。现在我通过在每个控制器操作中调用一个方法让其他系统验证会话来攻击它。我希望通过属性自动发生这种情况。
-
I am not. I query another system for that info ......好吧,您要求外部提供商对您的用户进行身份验证,但这是完全相同的事情,我担心。您应该考虑为外部认证部分创建一个新的中间件(它将在创建包含角色或允许用户查询的操作的新ClaimsPrincipal之前查询您的远程系统并检索用户的身份)并添加新的授权策略以确保呼叫者有一个角色/声明允许他/她提出请求。
-
以上链接不再起作用。
-
@BlakeNiemyjski修复。谢谢。
-
"ManageStore"字符串重复得太多了。
-
当达到指定策略的控制器时,建议的解决方案执行代码块。但是,根据intellisense提供的方法/属性,我推断它只是关于如何执行实际授权。我想知道在达成政策时如何做其他事情 - 例如我怎样才能将用户重定向到登录页面?是否可以不覆盖属性?
您可以创建自己的AuthorizationHandler,它将在Controllers和Actions上找到自定义属性,并将它们传递给HandleRequirementAsync方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public abstract class AttributeAuthorizationHandler <TRequirement, TAttribute > : AuthorizationHandler <TRequirement > where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
protected override Task HandleRequirementAsync (AuthorizationHandlerContext context, TRequirement requirement )
{
var attributes = new List <TAttribute >();
var action = (context .Resource as AuthorizationFilterContext )?.ActionDescriptor as ControllerActionDescriptor ;
if (action != null)
{
attributes .AddRange(GetAttributes (action .ControllerTypeInfo.UnderlyingSystemType));
attributes .AddRange(GetAttributes (action .MethodInfo));
}
return HandleRequirementAsync (context, requirement, attributes );
}
protected abstract Task HandleRequirementAsync (AuthorizationHandlerContext context, TRequirement requirement, IEnumerable <TAttribute > attributes );
private static IEnumerable <TAttribute > GetAttributes (MemberInfo memberInfo )
{
return memberInfo .GetCustomAttributes(typeof(TAttribute ), false).Cast<TAttribute >();
}
} |
然后,您可以将其用于控制??器或操作所需的任何自定义属性。例如,添加权限要求。只需创建自定义属性即可。
1 2 3 4 5 6 7 8 9 10
| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
public string Name { get; }
public PermissionAttribute(string name) : base("Permission")
{
Name = name;
}
} |
然后创建要添加到策略的要求
1 2 3 4
| public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
//Add any custom requirement properties if you have them
} |
然后为自定义属性创建AuthorizationHandler,继承我们之前创建的AttributeAuthorizationHandler。它将通过一个IEnumerable传递给HandleRequirementsAsync方法中的所有自定义属性,这些属性是从Controller和Action累积的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
{
foreach (var permissionAttribute in attributes)
{
if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
{
return;
}
}
context.Succeed(requirement);
}
private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
{
//Implement your custom user permission logic here
}
} |
最后,在Startup.cs ConfigureServices方法中,将自定义AuthorizationHandler添加到服务中,然后添加策略。
1 2 3 4 5 6 7 8 9
| services .AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler >();
services .AddAuthorization(options =>
{
options .AddPolicy("Permission", policyBuilder =>
{
policyBuilder .Requirements.Add(new PermissionAuthorizationRequirement ());
});
}); |
现在,您可以使用自定义属性简单地修饰控制器和操作。
1 2 3 4 5 6 7 8 9
| [Permission("AccessCustomers")]
public class CustomersController
{
[Permission("AddCustomer")]
IActionResult AddCustomer([FromBody] Customer customer)
{
//Add customer
}
} |
-
我会尽快看看这个。
-
这是过度设计的......我使用一个接收参数的简单AuthorizationFilterAttribute解决了这个问题。你不需要对此进行反思,它似乎比"官方"解决方案(我觉得很差)更具有人工性。
-
@ Vi100我在ASP.NET Core中找不到有关AuthorizationFilters的更多信息。官方文档页面说他们正在研究这个主题。 docs.microsoft.com/en-us/aspnet/core/security/authorization/…
-
@ Vi100请您分享您的解决方案,如果有更简单的方法来实现这一点,我很想知道。
-
@ Vi100我同意,如果您有解决方案,请分享。 Shawn是对的,现在有很多关于授权过滤器的文档,我们只看到"正在努力"
-
我实际上喜欢这个解决方案,它利用新的策略系统并结合属性来提供一个非常干净的解决方案。我使用全局Authorize属性来确保用户已登录,然后在需要时应用权限策略。
-
有一点要注意上面使用UnderlyingSystemType不能编译,但删除它似乎工作。
-
我喜欢这个解决方案。这正是我想要的。但是,如果@ Vi100或其他任何人找到了不需要反射的解决方案,我很乐意看到它。
-
我发现这个话题你可以看看它非常干净的stackoverflow.com/questions/41293420/…。希望能帮助到你
-
这有效!这是在ASP.Net Core中实现某种细粒度访问控制的唯一方法,这是非常荒谬的。
根据Derek Greer的回答,我用枚举做了。
这是我的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public enum PermissionItem
{
User,
Product,
Contact,
Review,
Client
}
public enum PermissionAction
{
Read,
Create,
}
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute (PermissionItem item, PermissionAction action )
: base(typeof(AuthorizeActionFilter ))
{
Arguments = new object[] { item, action };
}
}
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly PermissionItem _item ;
private readonly PermissionAction _action ;
public AuthorizeActionFilter (PermissionItem item, PermissionAction action )
{
_item = item ;
_action = action ;
}
public void OnAuthorization (AuthorizationFilterContext context )
{
bool isAuthorized = MumboJumboFunction (context .HttpContext.User, _item, _action ); // :)
if (!isAuthorized )
{
context .Result = new ForbidResult ();
}
}
}
public class UserController : BaseController
{
private readonly DbContext _context ;
public UserController ( DbContext context ) :
base()
{
_logger = logger ;
}
[Authorize (PermissionItem .User, PermissionAction .Read)]
public async Task <IActionResult > Index ()
{
return View (await _context .User.ToListAsync());
}
} |
-
谢谢你。我创建了这篇文章,其实现略有不同,并要求验证stackoverflow.com/questions/49551047/…