最近对咱们的服务进行了改造,本来内部服务在内部能够匿名调用,如今增长了限制,经过 identity server 来管理 api 和 client,网关和须要访问api的客户端或api服务相互调用经过 client_credencial
的方式来调用,这样一来咱们能够清晰知道哪些 api 服务会被哪些 api/client 所调用,并且安全性来讲更好。
为了保持后端服务的代码更好的兼容性,但愿可以实现相同的代码经过在 Startup 里不一样的配置实现不一样的 Authorization 逻辑,原来咱们的服务的 Authorize
都是以 Authorize("policyName")
的形式来写的,这样一来咱们只须要修改这个 Policy 的受权配置就能够了。对于 AllowAnonymous 就但愿能够经过一种相似的方式来实现,经过自定义一个 Policy 来实现本身的逻辑git
将 action 上的 AllowAnonymous
替换为 Authorize("policyName")
,在没有设置 Authorize
的 controller 上增长 Authorize("policyName")
github
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 配置:api
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) ;
访问原来的匿名接口安全
userId 为0访问原来的匿名接口app
userId 大于0访问原来的匿名接口asp.net
userId 为0访问须要登陆的接口
ide
userId 大于0访问须要登陆的接口
ui
注:按照上面的作法已经能够作到自定义 policy 代替 AllowAnonymous 的行为,可是原来返回的401,如今可能返回到就是 403 了this