Reputation: 3617
In an ASP.NET Core MVC application, we use Policy requirements for Authorization that are defined using SQL Server database roles. We call sp_helprolemember {roleName}
and then check if the User is a member to determine if they pass the requirement, example handler from the RequirementHandler:
// Get Role Members
IEnumerable<RoleMember> roleMembers = _roleMemberRepository.GetAllByRole(_roleName);
SecurityIdentifier userSid = new SecurityIdentifier(context.User.Claims.Where(c => c.Type == ClaimTypes.PrimarySid).SingleOrDefault().Value);
if (roleMembers.Any(rm => new SecurityIdentifier(rm.MemberSID, 0) == userSid))
{
context.Succeed(requirement);
}
MemberSID is a byte[]
array column from sp_helprolemember
This works, but my concern is querying the database each and every time we need to handle a requirement, which based on some code that's already written can be multiple times in one View.
Is it a better practice to cache this data somehow in memory to minimize database queries? I had the idea of using in-memory caching for each requirement handler to keep track of users that have previously been authorized and giving it a sliding window of a couple minutes before clearing, but I haven't worked with anything like that before and was wondering if there was a standard practice for doing something like this.
Upvotes: 0
Views: 838
Reputation: 62260
In order to avoid database access on every request, we normally save an user's authorized rolename as role claims inside Principal. It basically encrypts those claims, and saves it inside a cookie.
// Login
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName)
};
foreach (string roleName in authorizedRoleName)
{
claims.Add(new Claim(ClaimTypes.Role, roleName));
}
var identity = new ClaimsIdentity(claims, "local", "name", "role");
var principal = new ClaimsPrincipal(identity);
// Usage
if(context.User.HasClaim(ClaimTypes.Role, _roleName))
context.Succeed(requirement);
Upvotes: 1