asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

Intro

最近对我们的服务进行了改造,原本内部服务在内部可以匿名调用,现在增加了限制,通过 identity server 来管理 api 和 client,网关和需要访问api的客户端或api服务相互调用通过 client_credencial 的方式来调用,这样一来我们可以清晰知道哪些 api 服务会被哪些 api/client 所调用,而且安全性来说更好。为了保持后端服务的代码更好的兼容性,希望能够实现相同的代码通过在 Startup 里不同的配置实现不同的 Authorization 逻辑,原来我们的服务的 Authorize 都是以 Authorize("policyName") 的形式来写的,这样一来我们只需要修改这个 Policy 的授权配置就可以了。对于 AllowAnonymous 就希望可以通过一种类似的方式来实现,通过自定义一个 Policy 来实现自己的逻辑

实现方式

将 action 上的 AllowAnonymous 替换为 Authorize("policyName"),在没有设置 Authorize 的 controller 上增加 Authorize("policyName")


     
     
     
     
  1. public class AllowAnonymousPolicyTransformer : IApplicationModelConvention

  2. {

  3. private readonly string _policyName;

  4. public AllowAnonymousPolicyTransformer() : this("anonymous")

  5. {

  6. }

  7. public AllowAnonymousPolicyTransformer(string policyName) => _policyName = policyName;

  8. public void Apply(ApplicationModel application)

  9. {

  10. foreach (var controllerModel in application.Controllers)

  11. {

  12. if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AuthorizeFilter)))

  13. {

  14. foreach (var actionModel in controllerModel.Actions)

  15. {

  16. if (actionModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))

  17. {

  18. var allowAnonymousFilter = actionModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));

  19. actionModel.Filters.Remove(allowAnonymousFilter);

  20. actionModel.Filters.Add(new AuthorizeFilter(_policyName));

  21. }

  22. }

  23. }

  24. else

  25. {

  26. if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))

  27. {

  28. var allowAnonymousFilter = controllerModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));

  29. controllerModel.Filters.Remove(allowAnonymousFilter);

  30. }

  31. controllerModel.Filters.Add(new AuthorizeFilter(_policyName));

  32. }

  33. }

  34. }

  35. }

  36. public static class MvcBuilderExtensions

  37. {

  38. public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder)

  39. {

  40. builder.Services.Configure<MvcOptions>(options =>

  41. {

  42. options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer());

  43. });

  44. return builder;

  45. }

  46. public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder, string policyName)

  47. {

  48. builder.Services.Configure<MvcOptions>(options =>

  49. {

  50. options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer(policyName));

  51. });

  52. return builder;

  53. }

  54. }

controller 中的代码:


     
     
     
     
  1. [Route("api/[controller]")]

  2. public class ValuesController : Controller

  3. {

  4. private readonly ILogger _logger;

  5. public ValuesController(ILogger<ValuesController> logger)

  6. {

  7. _logger = logger;

  8. }

  9. // GET api/values

  10. [HttpGet]

  11. public ActionResult<IEnumerable> Get()

  12. {

  13. var msg = $"IsAuthenticated: {User.Identity.IsAuthenticated} ,UserName: {User.Identity.Name}";

  14. _logger.LogInformation(msg);

  15. return new string[] { msg };

  16. }

  17. // GET api/values/5

  18. [Authorize]

  19. [HttpGet("{id:int}")]

  20. public ActionResult Get(int id)

  21. {

  22. return "value";

  23. }

  24. // ...

  25. }

Startup 中 ConfigureServices 配置:


     
     
     
     
  1. var anonymousPolicyName = "anonymous";

  2. services.AddAuthorization(options =>

  3. {

  4. options.AddPolicy(anonymousPolicyName, builder => builder.RequireAssertion(context => context.User.Identity.IsAuthenticated));

  5. options.DefaultPolicy = new AuthorizationPolicyBuilder(HeaderAuthenticationDefaults.AuthenticationSchema)

  6. .RequireAuthenticatedUser()

  7. .RequireAssertion(context => context.User.GetUserId() > 0)

  8. .Build();

  9. });

  10. services.AddMvc(options =>

  11. {

  12. options.Conventions.Add(new ApiControllerVersionConvention());

  13. })

  14. .AddAnonymousPolicyTransformer(anonymousPolicyName)

  15. ;

实现效果

访问原来的匿名接口

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为_第1张图片

userId 为0访问原来的匿名接口

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为_第2张图片

userId 大于0访问原来的匿名接口

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为_第3张图片

userId 为0访问需要登录的接口asp.net core 自定义 Policy 替换 AllowAnonymous 的行为_第4张图片

userId 大于0访问需要登录的接口asp.net core 自定义 Policy 替换 AllowAnonymous 的行为_第5张图片

More

注:按照上面的做法已经可以做到自定义 policy 代替 AllowAnonymous 的行为,但是原来返回的401,现在可能返回到就是 403 了

Reference

  • https://github.com/WeihanLi/AspNetCorePlayground/blob/master/TestWebApplication

你可能感兴趣的:(asp.net core 自定义 Policy 替换 AllowAnonymous 的行为)