在ASP.NET 5
中,假设我有以下控制器:
[Route("api/[controller]")]
[Authorize(Roles = "Super")]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
}
然后假设我有一个用户 Jack,他已登录,但不是超级角色的成员。
当 Jack 尝试访问此资源时,他的访问被拒绝,正如预期的那样。
然后,在另一台机器上,我使用用户管理器将他添加到超级角色。
当 Jack 再次尝试访问此资源时,在他注销并重新登录之前,它仍会被拒绝访问。
如何确保每个用户的角色未缓存,或者在进行更改时以某种方式刷新?
所以这就是我最终设法解决这个问题的方法,但实际上这应该用安全印章来解决。下面的解决方案至少在授权发生之前提供了"查看"。
我创建了一个新AuthorizationHandler
,它在其构造函数中接受UserManager
:
public class SuperRequirement : AuthorizationHandler<SuperRequirement>, IAuthorizationRequirement
{
public UserManager<ApplicationUser> UserManager { get; set; }
public SuperRequirement(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
protected override void Handle(AuthorizationContext context, SuperRequirement requirement)
{
throw new System.NotImplementedException();
}
protected override async Task HandleAsync(AuthorizationContext context, SuperRequirement requirement)
{
var userName = context.User.Identity.Name;
var pass = await UserManager.IsInRoleAsync(await UserManager.FindByNameAsync(userName), "Super");
if (pass)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
然后我在Startup.cs
中配置了这个:
public void ConfigureServices(IServiceCollection services)
{
...
var sp = services.BuildServiceProvider();
//var service = sp.GetService<ISomeService>();
services.AddAuthorization(options =>
{
options.AddPolicy("Super",
policy =>
{
var userManager = sp.GetService<UserManager<ApplicationUser>>();
policy.Requirements.Add(new SuperRequirement(userManager));
});
});
}
最后,我更新了控制器以使用新的授权策略:
[Route("api/[controller]")]
[Authorize(Policy = "Super")]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
}
通过这种方式,它会检查数据库的每个请求以确保角色对齐,并使用UserManager
执行此操作。
请注意,这效率较低,因为每个 API 请求现在都会触发对数据库的额外调用,因此您应该实现某种缓存和失效机制;这只是为了演示原理。