我已经实现了一个解决方案,允许我阻止同一帐户的多个用户会话。
为此,我在ConfigureServices
方法中添加了以下配置:
services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<SoccerForecastContext>()
.AddDefaultTokenProviders();
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddRequirements(new ValidSessionRequirement())
.Build();
services.AddAuthorization(options =>
{
options.DefaultPolicy = defaultPolicy;
});
services.AddScoped<IUserClaimsPrincipalFactory<User>, ApplicationClaimsPrincipalFactory>();
services.AddTransient<IAuthorizationHandler, ValidSessionHandler>();
基本上对于管道将调用的每个请求
public class ApplicationClaimsPrincipalFactory : UserClaimsPrincipalFactory<User>
{
private readonly UserManager<User> _userManager;
public ApplicationClaimsPrincipalFactory(UserManager<User> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor)
{
_userManager = userManager;
}
public async override Task<ClaimsPrincipal> CreateAsync(User user)
{
var claims = await _userManager.GetClaimsAsync(user);
var session = claims.Where(e => e.Type == "session");
await _userManager.RemoveClaimsAsync(user, session);
await _userManager.AddClaimAsync(user, new Claim("session", Guid.NewGuid().ToString()));
var principal = await base.CreateAsync(user);
return principal;
}
}
类ValidSessionRequirement
简单继承:
public class ValidSessionRequirement : IAuthorizationRequirement
{
}
现在,如果我称这种方法:
[HttpGet]
[Authorize(Roles = "Customer, Admin, SuperAdmin")]
public async Task<IActionResult> Profile()
{
我得到:AccessDenied
,但用户有角色SuperAdmin
,如果我删除上面的逻辑所有工作按预期,任何想法?
当您调用Authorize
时,无论您应用于该方法的角色是什么,都会调用ValidSessionHandler
,因为它是您的默认策略。您可以添加一个检查以跳过ValidSessionHanlder
中的某些角色。下面的代码将跳过Admin
和SuperAdmin
的会话检查。
public class ValidSessionHandler : AuthorizationHandler<ValidSessionRequirement>
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
public ValidSessionHandler(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
{
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
_signInManager = signInManager ?? throw new ArgumentNullException(nameof(signInManager));
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidSessionRequirement requirement)
{
// if the user isn't authenticated then no need to check session
if (!context.User.Identity.IsAuthenticated)
return;
// get the user and session claim
var user = await _userManager.GetUserAsync(context.User);
var claims = await _userManager.GetClaimsAsync(user);
// get roles and skip check if Admin or SuperAdmin
var roles = await _userManager.GetRolesAsync(user);
foreach(var role in roles)
{
if(role == "Admin" || role == "SuperAdmin")
{
context.Succeed(requirement);
return;
}
}
var serverSession = claims.First(e => e.Type == "session");
var clientSession = context.User.FindFirst("session");
// if the client session matches the server session then the user is authorized
if (serverSession?.Value == clientSession?.Value)
{
context.Succeed(requirement);
}
return;
}
}
这是如何配置和适用于基于策略的授权。
在Startup
类配置策略
public void ConfigureServices(IServiceCollection services)
{
// ...
// Configure security policies
services.AddAuthorization(options =>
{
options.AddPolicy("SuperAdmins", policy => policy.RequireRole("SuperAdmin"));
options.AddPolicy("Admins", policy => policy.RequireRole("Admin", "SuperAdmin"));
options.AddPolicy("Customers", policy => policy.RequireRole("Customer", "Admin", "SuperAdmin"));
});
// ...
services.AddScoped<IUserClaimsPrincipalFactory<User>, MyUserClaimsFactory>();
// ...
}
在您的UserClaimsPrincipalFactory
中,您可以添加以下自定义声明:
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(User user)
{
var userId = user.Id;
user = await UserManager.Users.SingleAsync(u => u.Id == userId);
// Add role claims
var identity = await base.GenerateClaimsAsync(user);
// Add custom claims for application user properties we want to store in claims (in cookies) which allows to get common values on UI without DB hit)
identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName ?? ""));
identity.AddClaim(new Claim(ClaimTypes.Surname, user.LastName ?? ""));
// Add your session or any other claims here if needed
return identity;
}
在您的控制器授权属性中应使用Policy
名称,如下所示:
[Authorize(Policy = "SuperAdmins")]
public async Task<IActionResult> Profile()
{ ... }