如何在不查找 AspNetUserRoles 表的情况下在 WebAPI 方法中获取用户角色? [英] How can I get a users role inside a WebAPI method without a lookup to the AspNetUserRoles table?

查看:23
本文介绍了如何在不查找 AspNetUserRoles 表的情况下在 WebAPI 方法中获取用户角色?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a stored procedure that updates status. Depending on the role of the user the stored procedure has code that may or may not allow the status change. For this reason I need to pass a role name into a stored procedure. My role name is stored on the client in my javascript code but of course I need a second check on the server. Each user has only one of three roles and when requesting an update of status I can call one of three methods depending on the role that the client has. Here's what I tried.

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

I am using WebApi with bearer Token based authentication and ASP.NET Identity 2.1 and the application always runs in a browser. My users have been set up with the appropriate roles.

我放置了一些代码来获取用户 ID,然后转到 AspNetUserRoles 表以获取方法开始时的角色.但是我注意到这需要大约 500 毫秒才能运行.作为替代方案,我正在考虑以下事项:

I put in place some code to get the userId and then go to the AspNetUserRoles table to get the role at the start of a method. However I noticed this takes around 500 milliseconds to run. As an alternative I am considering the following:

    [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
    }

这是一种有效的方法,还是有另一种更干净的方法.我不太清楚的是前端还是后端缓存了用户角色.我假设这已经完成,或者有一些设置允许这样做.

Is this an efficient way to do this or is there perhaps another more clean way. What I am rather unclear on is if the front or back end caches the users role. I assume that this is done or there is some setting that will allow this to be done.

请注意,我在此处使用声明将角色信息发送给我的客户:

Note I am using claims to send the Role information to my client here:

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);
        }

然而,我在这里的问题与服务器有关,以及如果用户在不访问数据库的情况下处于特定角色,我如何检查我的方法.也许有一种方法可以通过声明安全地做到这一点,但我不知道该怎么做.

However my question here relates to the server and how I can check in my method if a user is in a certain role without going to the database. Maybe there's a way to do this securely with claims but I don't know how to do that.

任何帮助和建议将不胜感激.

Any help and advice would be much appreciated.

推荐答案

正如您所说,您正在使用不记名令牌来保护您的端点.我相信对于那些不记名令牌魔法字符串包含在其中的内容几乎没有误解.那么这些令牌包含您为其颁发令牌的用户的所有角色,以及如果您在 Web API 中使用默认数据保护 DPAPI 而不是(JWT 令牌),那么这些令牌将被签名和加密,因此没有人可以篡改数据在token里面除非他有mashineKey给web服务器发这个token,所以不用担心数据保护.

As you stated you are using bearer tokens to protect your end points. I believe that there is little misunderstanding with what those bearer tokens magical string contains inside it. Well those tokens contains all the roles for the user you issued the token for, as well if you are using the default data protection DPAPI in Web API not (JWT Tokens) then those tokens are signed and encrypted so no one can tamper with the data inside the token unless he has the mashineKey for the web server issued this token, so do not worry about data protection.

我的建议是从数据库中读取用户的角色/声明,不需要你尝试做的这种变通方法和黑客,你需要做的就是在用户登录时为用户设置声明在方法 GrantResourceOwnerCredentials 中,您可以通过获取用户然后从数据库读取角色并将它们设置为角色"类型的声明来像这样设置它

My recommendation is to read the roles/claims for the user from the database, there is no need for this workarounds and hacks you are trying to do, all you need to do is to set the claims for the users when they login in method GrantResourceOwnerCredentials You can set it like this way by getting the user then reading the roles from DB and setting them as claim of type "Role"

 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"));

记住这只会在用户登录时发生一次,然后您将收到一个不记名签名和电子加密的令牌,其中包含该用户的所有声明,无需任何数据库访问来验证它.

Remember this only happens once when the user login, then you will receive a bearer signed and ecrypted token which contains all the claims for this user, no need to any DB access to verify it.

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

Or if you want to create the identity from Database you can use the below:

 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 中执行以下操作:

Then in GrantResourceOwnerCredentials do the below:

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

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

Now once you send the bearer token to an protected end point with Authorize attribute such as [Authorize(Roles = "Teacher")] I can assure you that your code will not go to the DB to do any query open SQL profiler and check it will read the claims from the encrypted token along with the Roles claim and check if this user belongs to Teacher role and allow or deny the request.

我在博客上写了 5 篇关于 基于令牌的身份验证 以及 授权服务器JWT 令牌.我建议您阅读这些帖子以更好地了解不记名令牌.

I've blogged a detailed series of 5 posts about Token Based Authentication along with Authorization server, and JWT tokens. I recommend you to read those posts to get better understanding of bearer tokens.

这篇关于如何在不查找 AspNetUserRoles 表的情况下在 WebAPI 方法中获取用户角色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆