jlt*_*rem 362 c# asp.net authorization asp.net-core-mvc asp.net-core
我正在尝试在ASP.NET Core中创建自定义授权属性.在以前的版本中,可以覆盖bool AuthorizeCore(HttpContextBase httpContext)
.但这不再存在于AuthorizeAttribute
.
制作自定义AuthorizeAttribute的当前方法是什么?
我想要完成的任务:我在Header Authorization中收到一个会话ID.从该ID我将知道特定动作是否有效.
Der*_*eer 362
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提供表达给定控制器或操作的声明要求的简单方法的实现:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
blo*_*art 236
我是asp.net安全人员.首先让我道歉,在音乐商店样本或单元测试之外,这些都没有被记录,并且它们仍然在暴露的API方面得到了改进.详细文档在这里.
我们不希望您编写自定义授权属性.如果你需要这样做我们做错了什么.相反,你应该写授权要求.
授权作用于身份.身份通过身份验证创建.
您在评论中说要检查标题中的会话ID.您的会话ID将成为身份的基础.如果您想使用该Authorize
属性,您需要编写一个身份验证中间件来获取该标头并将其转换为经过身份验证的标头ClaimsPrincipal
.然后,您将在授权要求中检查该内容.授权要求可以像您一样复杂,例如,这是一个对当前身份提出出生日期索赔的要求,并且如果用户超过18岁则会授权;
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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在你的ConfigureServices()
功能中,你将它连接起来
services.AddAuthorization(options =>
{
options.AddPolicy("Over18",
policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
});
Run Code Online (Sandbox Code Playgroud)
最后将其应用于控制器或操作方法
[Authorize(Policy = "Over18")]
Run Code Online (Sandbox Code Playgroud)
giu*_*ius 85
看来,使用ASP.NET Core 2,你可以再次继承AuthorizeAttribute
,你只需要实现IAuthorizationFilter
:
[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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*lex 41
我决定添加另一个简单的答案。B/c 我发现这些答案中的大多数都有点过度设计。还因为我需要一种授予授权的方法,而不仅仅是拒绝它。这里的大多数答案都提供了一种“加强”安全性的方法,但我想“放松”它。例如:“如果配置了某些应用程序设置,则允许匿名用户访问”。
public class MyAuthAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//check access
if (CheckPermissions())
{
//all good, add optional code if you want. Or don't
}
else
{
//DENIED!
//return "ChallengeResult" to redirect to login page (for example)
context.Result = new ChallengeResult(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
Run Code Online (Sandbox Code Playgroud)
无需搞乱“政策”、“索赔”、“处理程序”和其他[嘟嘟]
用法:
// GET api/Get/5
[MyAuth]
public ActionResult<string> Get(int id)
{
return "blahblah";
}
Run Code Online (Sandbox Code Playgroud)
@Nikstr 在评论中询问了如何使用依赖注入。一探究竟:
//basically same class as above, but just a filter, not an attribute
public class MyAuthFilter : IAuthorizationFilter
{
private SomeService _service; //let's say you need a service for your auth
//dependency ibjection in constructor
public MyAuthAttribute(SomeService service)
{
_service = service;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//check access
if (CheckPermissions())
{
//all good, add optional code if you want. Or don't
}
else
{
//DENIED!
context.Result = new ChallengeResult(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在确保您在 Startup.cs 中注册此服务,例如.AddScoped(blahblah)
但用法略有不同:
// GET api/Get/5
[TypeFilter<MyAuthFilter>] // <-- note this
public ActionResult<string> Get(int id)
Run Code Online (Sandbox Code Playgroud)
TypeFilter
只是告诉框架每次实例化属性并在这样做时使用 DI。只需谷歌搜索“TypeFilterAttribute”文档即可获取更多信息
如果你不喜欢这条线的丑陋,你可以像这样[TypeFilter<MyAuthFilter>]
简单地继承TypeFilter
//empty one-liner "wrapper" class that does nothing
public class MyAuthAttribute : TypeFilterAttribute<MyAuthFilter> { }
Run Code Online (Sandbox Code Playgroud)
然后像以前一样使用它
[MyAuth] // <-- looks simpler huh
public ActionResult<string> Get(int id)
Run Code Online (Sandbox Code Playgroud)
Sha*_*awn 26
您可以创建自己的AuthorizationHandler,它将在Controllers和Actions上找到自定义属性,并将它们传递给HandleRequirementAsync方法.
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>();
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以将其用于控制器或操作所需的任何自定义属性.例如,添加权限要求.只需创建自定义属性即可.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
public string Name { get; }
public PermissionAttribute(string name) : base("Permission")
{
Name = name;
}
}
Run Code Online (Sandbox Code Playgroud)
然后创建要添加到策略的要求
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
//Add any custom requirement properties if you have them
}
Run Code Online (Sandbox Code Playgroud)
然后为自定义属性创建AuthorizationHandler,继承我们之前创建的AttributeAuthorizationHandler.这将传递一个IEnumerable所有在HandleRequirementsAsync方法,从您的控制器和行动积累了您的自定义属性.
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
}
}
Run Code Online (Sandbox Code Playgroud)
最后,在Startup.cs ConfigureServices方法中,将自定义AuthorizationHandler添加到服务中,然后添加策略.
services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policyBuilder =>
{
policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
});
});
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用自定义属性简单地修饰控制器和操作.
[Permission("AccessCustomers")]
public class CustomersController
{
[Permission("AddCustomer")]
IActionResult AddCustomer([FromBody] Customer customer)
{
//Add customer
}
}
Run Code Online (Sandbox Code Playgroud)
bru*_*ida 24
基于德里克·格里尔伟大的答案,我用枚举做到了.
这是我的代码示例:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
Kév*_*let 23
制作自定义AuthorizeAttribute的当前方法是什么
容易:不要创建自己的AuthorizeAttribute
.
对于纯授权方案(如仅限制对特定用户的访问),建议的方法是使用新的授权块:https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84 -L92
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() { ... }
}
Run Code Online (Sandbox Code Playgroud)
对于身份验证,最好在中间件级别处理.
你想要完成什么?
PPa*_*ann 12
现代方式是 AuthenticationHandlers
在startup.cs中添加
services.AddAuthentication("BasicAuthentication").AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IUserService _userService;
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IUserService userService)
: base(options, logger, encoder, clock)
{
_userService = userService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header");
User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
user = await _userService.Authenticate(username, password);
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}
if (user == null)
return AuthenticateResult.Fail("Invalid User-name or Password");
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
Run Code Online (Sandbox Code Playgroud)
IUserService 是您在拥有用户名和密码的地方创建的服务。基本上它返回一个用户类,用于映射您的声明。
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};
Run Code Online (Sandbox Code Playgroud)
然后你可以查询这些声明和她你映射的任何数据,有很多,看看ClaimTypes类
您可以在扩展方法中使用它并获取任何映射
public int? GetUserId()
{
if (context.User.Identity.IsAuthenticated)
{
var id=context.User.FindFirst(ClaimTypes.NameIdentifier);
if (!(id is null) && int.TryParse(id.Value, out var userId))
return userId;
}
return new Nullable<int>();
}
Run Code Online (Sandbox Code Playgroud)
这种新方式,我认为比这里显示的旧方式更好,两者都有效
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization != null)
{
var authToken = actionContext.Request.Headers.Authorization.Parameter;
// decoding authToken we get decode value in 'Username:Password' format
var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
// spliting decodeauthToken using ':'
var arrUserNameandPassword = decodeauthToken.Split(':');
// at 0th postion of array we get username and at 1st we get password
if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1]))
{
// setting current principle
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null);
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
public static bool IsAuthorizedUser(string Username, string Password)
{
// In this method we can handle our database logic here...
return Username.Equals("test") && Password == "test";
}
}
Run Code Online (Sandbox Code Playgroud)
下面的代码在 .Net Core 5 中对我有用
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AccessAuthorizationAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string Module { get; set; } //Permission string to get from controller
public AccessAuthorizationAttribute(string module)
{
Module = module;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//Validate if any permissions are passed when using attribute at controller or action level
if (string.IsNullOrEmpty(Module))
{
//Validation cannot take place without any permissions so returning unauthorized
context.Result = new UnauthorizedResult();
return;
}
if (hasAccess)
{
return;
}
context.Result = new UnauthorizedResult();
return;
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人只想使用当前的安全实践在授权阶段验证不记名令牌,您可以,
将此添加到您的 Startup/ConfigureServices
services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
services.AddAuthorization(options => options.AddPolicy("Bearer",
policy => policy.AddRequirements(new BearerRequirement())
)
);
Run Code Online (Sandbox Code Playgroud)
这在你的代码库中,
public class BearerRequirement : IAuthorizationRequirement
{
public async Task<bool> IsTokenValid(SomeValidationContext context, string token)
{
// here you can check if the token received is valid
return true;
}
}
public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement>
{
public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject)
{
...
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement)
{
var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.Contains("Bearer"))
{
var token = authHeader.Replace("Bearer ", string.Empty);
if (await requirement.IsTokenValid(thatYouCanInject, token))
{
context.Succeed(requirement);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果代码没有到达context.Succeed(...)
它无论如何都会失败(401)。
然后在你的控制器中你可以使用
[Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Run Code Online (Sandbox Code Playgroud)
截至撰写本文时,我相信这可以通过 asp.net core 2 及更高版本中的 IClaimsTransformation 接口来完成。我刚刚实现了一个概念证明,该概念证明足以在此处发布。
\n\npublic class PrivilegesToClaimsTransformer : IClaimsTransformation\n{\n private readonly IPrivilegeProvider privilegeProvider;\n public const string DidItClaim = "http://foo.bar/privileges/resolved";\n\n public PrivilegesToClaimsTransformer(IPrivilegeProvider privilegeProvider)\n {\n this.privilegeProvider = privilegeProvider;\n }\n\n public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)\n {\n if (principal.Identity is ClaimsIdentity claimer)\n {\n if (claimer.HasClaim(DidItClaim, bool.TrueString))\n {\n return principal;\n }\n\n var privileges = await this.privilegeProvider.GetPrivileges( ... );\n claimer.AddClaim(new Claim(DidItClaim, bool.TrueString));\n\n foreach (var privilegeAsRole in privileges)\n {\n claimer.AddClaim(new Claim(ClaimTypes.Role /*"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" */, privilegeAsRole));\n }\n }\n\n return principal;\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n要在您的控制器中使用它,只需[Authorize(Roles="whatever")]
在您的方法中添加适当的内容即可。
[HttpGet]\n[Route("poc")]\n[Authorize(Roles = "plugh,blast")]\npublic JsonResult PocAuthorization()\n{\n var result = Json(new\n {\n when = DateTime.UtcNow,\n });\n\n result.StatusCode = (int)HttpStatusCode.OK;\n\n return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n在我们的例子中,每个请求都包含一个 JWT 授权标头。这是原型,我相信下周我们将在我们的生产系统中做一些与此非常接近的事情。
\n\n未来的选民,投票时请考虑写作日期。截至今天,这个works on my machine.
\xe2\x84\xa2 您可能需要更多的错误处理和日志记录您的实现。
小智 6
这是一个简单的 5 步指南,介绍如何使用所有复制和粘贴的策略来实现自定义角色授权:)。我使用了这些文档。
创建需求:
public class RoleRequirement : IAuthorizationRequirement
{
public string Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
创建一个处理程序:
public class RoleHandler : AuthorizationHandler<RoleRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
var requiredRole = requirement.Role;
//custom auth logic
// you can use context to access authenticated user,
// you can use dependecy injection to call custom services
var hasRole = true;
if (hasRole)
{
context.Succeed(requirement);
}
else
{
context.Fail(new AuthorizationFailureReason(this, $"Role {requirement.Role} missing"));
}
}
}
Run Code Online (Sandbox Code Playgroud)
在 Program.cs 中添加处理程序:
builder.Services.AddSingleton<IAuthorizationHandler, RoleHandler>();
Run Code Online (Sandbox Code Playgroud)
在program.cs中添加包含您的角色要求的策略:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Read", policy => policy.Requirements.Add(new RoleRequirement{Role = "ReadAccess_Custom_System"}));
});
Run Code Online (Sandbox Code Playgroud)
使用您的政策:
[Authorize("Read")]
public class ExampleController : ControllerBase
{
}
Run Code Online (Sandbox Code Playgroud)
已接受的答案(/sf/answers/2894375361/)实际上不可维护或不合适,因为“CanReadResource”被用作声明(但实际上应该是一项政策,IMO)。答案中的方法在使用方式上并不好,因为如果一个操作方法需要许多不同的声明设置,那么对于该答案,您将不得不重复编写类似...
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[ClaimRequirement(MyClaimTypes.AnotherPermision, "AnotherClaimVaue")]
//and etc. on a single action.
Run Code Online (Sandbox Code Playgroud)
所以,想象一下这需要多少编码。理想情况下,“CanReadResource”应该是一种使用许多声明来确定用户是否可以读取资源的策略。
我所做的是将我的策略创建为枚举,然后循环并设置像这样的要求......
services.AddAuthorization(authorizationOptions =>
{
foreach (var policyString in Enum.GetNames(typeof(Enumerations.Security.Policy)))
{
authorizationOptions.AddPolicy(
policyString,
authorizationPolicyBuilder => authorizationPolicyBuilder.Requirements.Add(new DefaultAuthorizationRequirement((Enumerations.Security.Policy)Enum.Parse(typeof(Enumerations.Security.Policy), policyWrtString), DateTime.UtcNow)));
/* Note that thisn does not stop you from
configuring policies directly against a username, claims, roles, etc. You can do the usual.
*/
}
});
Run Code Online (Sandbox Code Playgroud)
DefaultAuthorizationRequirement 类看起来像...
public class DefaultAuthorizationRequirement : IAuthorizationRequirement
{
public Enumerations.Security.Policy Policy {get; set;} //This is a mere enumeration whose code is not shown.
public DateTime DateTimeOfSetup {get; set;} //Just in case you have to know when the app started up. And you may want to log out a user if their profile was modified after this date-time, etc.
}
public class DefaultAuthorizationHandler : AuthorizationHandler<DefaultAuthorizationRequirement>
{
private IAServiceToUse _aServiceToUse;
public DefaultAuthorizationHandler(
IAServiceToUse aServiceToUse
)
{
_aServiceToUse = aServiceToUse;
}
protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultAuthorizationRequirement requirement)
{
/*Here, you can quickly check a data source or Web API or etc.
to know the latest date-time of the user's profile modification...
*/
if (_aServiceToUse.GetDateTimeOfLatestUserProfileModication > requirement.DateTimeOfSetup)
{
context.Fail(); /*Because any modifications to user information,
e.g. if the user used another browser or if by Admin modification,
the claims of the user in this session cannot be guaranteed to be reliable.
*/
return;
}
bool shouldSucceed = false; //This should first be false, because context.Succeed(...) has to only be called if the requirement specifically succeeds.
bool shouldFail = false; /*This should first be false, because context.Fail()
doesn't have to be called if there's no security breach.
*/
// You can do anything.
await doAnythingAsync();
/*You can get the user's claims...
ALSO, note that if you have a way to priorly map users or users with certain claims
to particular policies, add those policies as claims of the user for the sake of ease.
BUT policies that require dynamic code (e.g. checking for age range) would have to be
coded in the switch-case below to determine stuff.
*/
var claims = context.User.Claims;
// You can, of course, get the policy that was hit...
var policy = requirement.Policy
//You can use a switch case to determine what policy to deal with here...
switch (policy)
{
case Enumerations.Security.Policy.CanReadResource:
/*Do stuff with the claims and change the
value of shouldSucceed and/or shouldFail.
*/
break;
case Enumerations.Security.Policy.AnotherPolicy:
/*Do stuff with the claims and change the
value of shouldSucceed and/or shouldFail.
*/
break;
// Other policies too.
default:
throw new NotImplementedException();
}
/* Note that the following conditions are
so because failure and success in a requirement handler
are not mutually exclusive. They demand certainty.
*/
if (shouldFail)
{
context.Fail(); /*Check the docs on this method to
see its implications.
*/
}
if (shouldSucceed)
{
context.Succeed(requirement);
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,上面的代码还可以启用用户到数据存储中的策略的预映射。因此,在为用户编写声明时,您基本上是检索直接或间接预先映射到用户的策略(例如,因为用户具有特定声明值并且该声明值已被识别并映射到策略,例如它也为具有该声明值的用户提供自动映射),并将策略登记为声明,这样在授权处理程序中,您可以简单地检查用户的声明是否包含 require.Policy 作为他们的声明项的值索赔。这是用于满足策略要求的静态方式,例如“名字”要求本质上是相当静态的。所以,
[Authorize(Policy = nameof(Enumerations.Security.Policy.ViewRecord))]
Run Code Online (Sandbox Code Playgroud)
动态需求可以是关于检查年龄范围等,使用此类需求的策略不能预先映射到用户。
@blowdart ( /sf/answers/2202565921/ )已经给出了一个动态策略声明检查的例子(例如检查用户是否超过 18 岁)。
PS:我用手机打的。请原谅任何错别字和缺乏格式。
归档时间: |
|
查看次数: |
190298 次 |
最近记录: |