Asp.net 核心 web-api 基于 http 操作的授权
Asp.net core web-api http action based authorization
我正在尝试向 asp.net 核心 api 添加两个策略。
- ReadOnly - 只允许 get 调用
- AppUser - 允许获取、放置、post、删除
//// 政策在应用程序设置中 json
Policies.json
{
"name": "ReadOnly"
"ids":["user1", "user2"],
}
,
{
"name": "AppUser"
"ids":["user3", "user4"]
}
//// 要求
public class IdRequirement : IAuthorizationRequirement
{
public string Name { get; set; }
public List<string> Ids { get; set; } = new List<string>();
public IdRequirement(string name, List<string> ids)
{
Name = name;
Ids = ids;
}
}
//// 处理程序
public class IdHandler : AuthorizationHandler<IdRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IdRequirement requirement)
{
if (context.User.Identity.IsAuthenticated)
{
string currentUserid = GetId(context.User);
if (requirement.Ids != null && requirement.Ids.Any(x => x == currentUserid))
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
//// 在服务中
services.AddAuthorization(options =>
{
var reqList = configuration.GetSection("Policies").Get<List<IdRequirement>>();
//// app users must have readonly access by default. ReadOnly is the default policy.
//// not sure this is the correct way?
var appUserReq = reqList.Find(x => x.Name.Equals("AppUser", StringComparison.Ordinal));
var readOnlyReq = reqList.Find(x => x.Name.Equals("ReadOnly", StringComparison.Ordinal));
readOnlyReq.Ids = readOnlyReq.Ids.Union(appUserReq.ids).ToList();
reqList.ForEach(requirement =>
{
options.AddPolicy(requirement.Name, policy =>
{
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.Requirements.Add(requirement);
});
});
});
//// 在配置中
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization("ReadOnly");
});
//// 控制器
public class TestController : BaseController
{
// GET: userid
[HttpGet("testget")]
public async Task<IActionResult> GetSomething()
{
return Ok();
}
[Authorize(Policy = "AppUser")]
[HttpPost("testpost")]
public async Task<IActionResult> PostSomething()
{
return Ok();
}
}
如果我调用testget,它只会授权只读需求。这对我来说是正确的。
如果我调用testpost,它会先授权appuser需求,然后再调用readonly需求。
我不想在使用 [Authorize(Policy = "AppUser")] 时触发只读要求
- 我是漏掉了还是误用了概念?
- 这是默认行为吗?
我引用了这篇文章
https://github.com/blowdart/AspNetAuthorizationWorkshop
你没有遗漏任何东西。将 [Authorize(Policy = "AppUser")]
放在操作上时,它将首先通过此策略 AppUser
进行身份验证。如果不成功,它将重定向到 AccessDeniedPath
。这时会经过端点,端点中作为全局授权的RequireAuthorization
会再次触发。如果不配置AccessDeniedPath
,会return404.
最终借助这个 的帮助,我取得了如下成就。确保您不再需要在控制器中添加 Authorize 属性。如果您想要特定控制器的任何自定义设置,可以修改 RequireAuthorizationForHttpMethods
。
var mutatingHttpMethods = new HashSet<HttpMethod>()
{
HttpMethod.Post,
HttpMethod.Put,
HttpMethod.Delete
};
var getHttpMethods = new HashSet<HttpMethod>()
{
HttpMethod.Get
};
endpoints
.MapControllers()
.RequireAuthorizationForHttpMethods(httpMethods => httpMethods.Any(httpMethod => getHttpMethods.Contains(httpMethod)), "ReadOnly")
.RequireAuthorizationForHttpMethods(httpMethods => httpMethods.Any(httpMethod => mutatingHttpMethods.Contains(httpMethod)), "AppUser");
我正在尝试向 asp.net 核心 api 添加两个策略。
- ReadOnly - 只允许 get 调用
- AppUser - 允许获取、放置、post、删除
//// 政策在应用程序设置中 json
Policies.json
{
"name": "ReadOnly"
"ids":["user1", "user2"],
}
,
{
"name": "AppUser"
"ids":["user3", "user4"]
}
//// 要求
public class IdRequirement : IAuthorizationRequirement
{
public string Name { get; set; }
public List<string> Ids { get; set; } = new List<string>();
public IdRequirement(string name, List<string> ids)
{
Name = name;
Ids = ids;
}
}
//// 处理程序
public class IdHandler : AuthorizationHandler<IdRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IdRequirement requirement)
{
if (context.User.Identity.IsAuthenticated)
{
string currentUserid = GetId(context.User);
if (requirement.Ids != null && requirement.Ids.Any(x => x == currentUserid))
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
//// 在服务中
services.AddAuthorization(options =>
{
var reqList = configuration.GetSection("Policies").Get<List<IdRequirement>>();
//// app users must have readonly access by default. ReadOnly is the default policy.
//// not sure this is the correct way?
var appUserReq = reqList.Find(x => x.Name.Equals("AppUser", StringComparison.Ordinal));
var readOnlyReq = reqList.Find(x => x.Name.Equals("ReadOnly", StringComparison.Ordinal));
readOnlyReq.Ids = readOnlyReq.Ids.Union(appUserReq.ids).ToList();
reqList.ForEach(requirement =>
{
options.AddPolicy(requirement.Name, policy =>
{
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.Requirements.Add(requirement);
});
});
});
//// 在配置中
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization("ReadOnly");
});
//// 控制器
public class TestController : BaseController
{
// GET: userid
[HttpGet("testget")]
public async Task<IActionResult> GetSomething()
{
return Ok();
}
[Authorize(Policy = "AppUser")]
[HttpPost("testpost")]
public async Task<IActionResult> PostSomething()
{
return Ok();
}
}
如果我调用testget,它只会授权只读需求。这对我来说是正确的。
如果我调用testpost,它会先授权appuser需求,然后再调用readonly需求。
我不想在使用 [Authorize(Policy = "AppUser")] 时触发只读要求
- 我是漏掉了还是误用了概念?
- 这是默认行为吗?
我引用了这篇文章 https://github.com/blowdart/AspNetAuthorizationWorkshop
你没有遗漏任何东西。将 [Authorize(Policy = "AppUser")]
放在操作上时,它将首先通过此策略 AppUser
进行身份验证。如果不成功,它将重定向到 AccessDeniedPath
。这时会经过端点,端点中作为全局授权的RequireAuthorization
会再次触发。如果不配置AccessDeniedPath
,会return404.
最终借助这个 RequireAuthorizationForHttpMethods
。
var mutatingHttpMethods = new HashSet<HttpMethod>()
{
HttpMethod.Post,
HttpMethod.Put,
HttpMethod.Delete
};
var getHttpMethods = new HashSet<HttpMethod>()
{
HttpMethod.Get
};
endpoints
.MapControllers()
.RequireAuthorizationForHttpMethods(httpMethods => httpMethods.Any(httpMethod => getHttpMethods.Contains(httpMethod)), "ReadOnly")
.RequireAuthorizationForHttpMethods(httpMethods => httpMethods.Any(httpMethod => mutatingHttpMethods.Contains(httpMethod)), "AppUser");