如何在不查找AspNetUserRoles表的情况下在WebAPI方法中获取用户角色



我有一个更新状态的存储过程。根据用户的角色,存储过程具有允许或不允许状态更改的代码。出于这个原因,我需要将角色名称传递到存储过程中。我的角色名称存储在客户端的javascript代码中,但当然我需要在服务器上进行第二次检查。每个用户只有三个角色中的一个,当请求更新状态时,我可以根据客户端的角色调用三个方法之一。这是我试过的。

我使用WebApi,带有基于承载令牌的身份验证和ASP.NET Identity 2.1,应用程序始终在浏览器中运行。我的用户已经设置了适当的角色。

我放了一些代码来获取userId,然后转到AspNetUserRoles表来获取方法开始时的角色。然而,我注意到这大约需要500毫秒才能运行。作为替代方案,我正在考虑以下内容:

    [HttpPut]
    [Authorize(Roles = "Admin")]
    [Route("AdminUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> AdminUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Admin");
    }
    [HttpPut]
    [Authorize(Roles = "Student")]
    [Route("StudentUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> StudentUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Student");
    }
    [HttpPut]
    [Authorize(Roles = "Teacher")]
    [Route("TeacherUpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> TeacherUpdateStatus(int userTestId, int userTestStatusId)
    {
        return await UpdateStatusMethod(userTestId, userTestStatusId, "Teacher");
    }
    private async Task<IHttpActionResult> UpdateStatusMethod(int userTestId, int userTestStatusId, string roleName)
    {
        // Call the stored procedure here and pass in the roleName
    }

这是一种有效的方法吗?或者还有其他更干净的方法吗。我不清楚的是前端还是后端缓存用户角色。我认为这已经完成,或者有一些设置可以允许这样做。

注意:我使用索赔将角色信息发送到我的客户这里:

public static AuthenticationProperties CreateProperties(
            string userName,
            ClaimsIdentity oAuthIdentity,
            string firstName,
            string lastName,
            int organization)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
                {
                    { "userName", userName},
                    { "firstName", firstName},
                    { "lastName", lastName},
                    { "organization", organization.ToString()},
                    { "roles",string.Join(":",oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())}
                };
            return new AuthenticationProperties(data);
        }

然而,我在这里的问题涉及服务器,以及如果用户在不访问数据库的情况下担任某个角色,我如何检查我的方法。也许有一种方法可以安全地处理索赔,但我不知道如何做到这一点。

如有任何帮助和建议,我们将不胜感激。

正如您所说,您正在使用承载令牌来保护您的端点。我相信,人们对魔术串中的承载符号并没有什么误解。这些令牌包含您为其颁发令牌的用户的所有角色,并且如果您在Web API not(JWT令牌)中使用默认的数据保护DPAPI,则这些令牌将被签名和加密,因此任何人都不能篡改令牌中的数据,除非他拥有颁发此令牌的Web服务器的mashineKey,因此不必担心数据保护。

我的建议是从数据库中读取用户的角色/声明,不需要你试图做的这些变通方法和技巧,你所需要做的就是在用户登录方法GrantResourceOwnerCredentials时为他们设置声明。你可以这样设置,让用户从DB中读取角色,并将其设置为"角色"类型的声明

 var identity = new ClaimsIdentity(context.Options.AuthenticationType);
 identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
 identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
 identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor"));

请记住,当用户登录时,这种情况只发生一次,然后您将收到一个承载签名和加密的令牌,其中包含该用户的所有声明,无需任何DB访问来验证它。

或者,如果你想从数据库中创建身份,你可以使用以下方法:

 public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
        // Add custom user claims here
        return userIdentity;
    }

然后在GrantResourceOwnerCredentials中执行以下操作:

ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);

现在,一旦您将承载令牌发送到具有Authorize属性(如[Authorize(Roles = "Teacher")])的受保护端点,我可以向您保证您的代码不会进入数据库进行任何查询打开SQL探查器并检查,它将从加密令牌中读取声明以及Roles声明,并检查此用户是否属于教师角色,并允许或拒绝请求。

我在博客上写了一系列关于基于令牌的身份验证、授权服务器和JWT令牌的详细文章,共5篇。我建议你阅读这些帖子,以更好地了解持牌代币。

您可以简单地检查控制器类中可用的User.IsInRole("RoleName"),而不是使用3个单独的方法。它使用的逻辑与[Authorise]属性中的逻辑相同。例如

public class DataApiController : ApiController
{
    [HttpPut]
    [Route("UpdateStatus/{userTestId:int}/{userTestStatusId:int}")]
    public async Task<IHttpActionResult> UpdateStatus(int userTestId, int userTestStatusId)
    {
        if(User.IsInRole("Admin"))
        {
            //Update method etc....
        }
        //else if(....) else etc
    {
}

角色声明默认存储在用户的登录cookie中,因此[授权(Roles="Teacher")]应该很快。唯一的数据库命中将是在您第一次登录时(或每当刷新登录cookie时)。授权检查是针对从登录cookie创建的IClaimsPrincipal进行的。

您可能还需要更新一些其他代码才能正常工作,请参阅这个问题,它与您正在做的类似:使用角色授权

相关内容

  • 没有找到相关文章

最新更新