asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
asp.net core 自定義 Policy 替換 AllowAnonymous 的行為
Intro
最近對(duì)我們的服務(wù)進(jìn)行了改造,原本內(nèi)部服務(wù)在內(nèi)部可以匿名調(diào)用,現(xiàn)在增加了限制,通過 identity server 來管理 api 和 client,網(wǎng)關(guān)和需要訪問api的客戶端或api服務(wù)相互調(diào)用通過 client_credencial 的方式來調(diào)用,這樣一來我們可以清晰知道哪些 api 服務(wù)會(huì)被哪些 api/client 所調(diào)用,而且安全性來說更好。為了保持后端服務(wù)的代碼更好的兼容性,希望能夠?qū)崿F(xiàn)相同的代碼通過在 Startup 里不同的配置實(shí)現(xiàn)不同的 Authorization 邏輯,原來我們的服務(wù)的 Authorize 都是以 Authorize("policyName") 的形式來寫的,這樣一來我們只需要修改這個(gè) Policy 的授權(quán)配置就可以了。對(duì)于 AllowAnonymous 就希望可以通過一種類似的方式來實(shí)現(xiàn),通過自定義一個(gè) Policy 來實(shí)現(xiàn)自己的邏輯
實(shí)現(xiàn)方式
將 action 上的 AllowAnonymous 替換為 Authorize("policyName"),在沒有設(shè)置 Authorize 的 controller 上增加 Authorize("policyName")
public class AllowAnonymousPolicyTransformer : IApplicationModelConvention
{
private readonly string _policyName;
public AllowAnonymousPolicyTransformer() : this("anonymous")
{
}
public AllowAnonymousPolicyTransformer(string policyName) => _policyName = policyName;
public void Apply(ApplicationModel application)
{
foreach (var controllerModel in application.Controllers)
{
if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AuthorizeFilter)))
{
foreach (var actionModel in controllerModel.Actions)
{
if (actionModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))
{
var allowAnonymousFilter = actionModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));
actionModel.Filters.Remove(allowAnonymousFilter);
actionModel.Filters.Add(new AuthorizeFilter(_policyName));
}
}
}
else
{
if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))
{
var allowAnonymousFilter = controllerModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));
controllerModel.Filters.Remove(allowAnonymousFilter);
}
controllerModel.Filters.Add(new AuthorizeFilter(_policyName));
}
}
}
}
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder)
{
builder.Services.Configure<MvcOptions>(options =>
{
options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer());
});
return builder;
}
public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder, string policyName)
{
builder.Services.Configure<MvcOptions>(options =>
{
options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer(policyName));
});
return builder;
}
}
controller 中的代碼:
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ILogger _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var msg = $"IsAuthenticated: {User.Identity.IsAuthenticated} ,UserName: {User.Identity.Name}";
_logger.LogInformation(msg);
return new string[] { msg };
}
// GET api/values/5
[Authorize]
[HttpGet("{id:int}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// ...
}
Startup 中 ConfigureServices 配置:
var anonymousPolicyName = "anonymous";
services.AddAuthorization(options =>
{
options.AddPolicy(anonymousPolicyName, builder => builder.RequireAssertion(context => context.User.Identity.IsAuthenticated));
options.DefaultPolicy = new AuthorizationPolicyBuilder(HeaderAuthenticationDefaults.AuthenticationSchema)
.RequireAuthenticatedUser()
.RequireAssertion(context => context.User.GetUserId<int>() > 0)
.Build();
});
services.AddMvc(options =>
{
options.Conventions.Add(new ApiControllerVersionConvention());
})
.AddAnonymousPolicyTransformer(anonymousPolicyName)
;
實(shí)現(xiàn)效果
訪問原來的匿名接口
userId 為0訪問原來的匿名接口
userId 大于0訪問原來的匿名接口
userId 為0訪問需要登錄的接口
userId 大于0訪問需要登錄的接口
More
注:按照上面的做法已經(jīng)可以做到自定義 policy 代替 AllowAnonymous 的行為,但是原來返回的401,現(xiàn)在可能返回到就是 403 了
Reference
https://github.com/WeihanLi/AspNetCorePlayground/blob/master/TestWebApplication
總結(jié)
以上是生活随笔為你收集整理的asp.net core 自定义 Policy 替换 AllowAnonymous 的行为的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EFCore批量操作,你真的清楚吗
- 下一篇: 前端小白在asp.net core mv