IdentityServer4之Claim(基于角色的权限控制)


IdentityServer4Claim(基于角色的权限控制)

前言

Claim是什么?每个用户都有多个Claim,每个Claim声明了用户的某个信息比如:Role=AdminUserID=1000等,这里RoleUserID每个都是用户的Claim,都是表示用户信息的单元 ,不妨就把它称为用户信息单元 。它使用一个字典进行存储,一个Key,一个Value

环境

Visual Studio 2019 + .net 5.0

IdentityServer4   (4.0.0)

Microsoft.AspNetCore.Authentication.JwtBearer (5.0.0)

IdentityModel (6.0.0)

实现步骤

创建IDS4项目

dotnet CLI快速安装模板Identityserver模板

dotnet new -i IdentityServer4.Templates

创建IdentityServery 项目

dotnet new is4empty -n IdentityServer_ResourceOwnerPasswordCredentials

注意项目名不可以取IdentityServery 4,不然会和IdentityServery 4.Dll引用重名。

引用IdentityServer4依赖

这里使用4.0.0版本,如图:

修改端口

此模板中使用的协议是https,在 Kestrel 上运行时端口设置为 5001 或在 IISExpress 上运行时设置为随机端口。可以在Properties\launchSettings.json文件中更改它。

  "profiles": {

    "SelfHost": {

      "commandName": "Project",

      "launchBrowser": true,

      "environmentVariables": {

        "ASPNETCORE_ENVIRONMENT": "Development"

      },

      "applicationUrl": "https://localhost:5001;http://localhost:5000"

    }

  }

这里修改为:

 "applicationUrl": "https://localhost:5001;http://localhost:5000"

定义API接口的活动范围API Scope

API 是系统中要保护的资源。资源定义可以通过多种方式加载。模板已经创建了 Config.cs。打开它,将代码更新为如下所示:

public static IEnumerable ApiScopes =>

            new ApiScope[]

            { new ApiScope("api1", "My API",new List(){JwtClaimTypes.Role})};

添加用户

就像基于内存存储的资源(即范围 Scopes)和客户端一样,对于用户也可以这样做。

TestUser类型表示一个测试用户及其身份信息。 Config.cs文件中添加以下代码以创建用户:

            public static List GetUsers()

        {

            return new List

            {

                new TestUser

                {

                    SubjectId = "1",

                    Username = "alice",

                    Password = "password",

                    Claims = new List(){new Claim(JwtClaimTypes.Role,"superadmin") }

                },

                new TestUser

                {

                    SubjectId = "2",

                    Username = "bob",

                    Password = "password",

                    Claims = new List(){new Claim(JwtClaimTypes.Role,"superadmin") }

                },

                new TestUser

                {

                    SubjectId = "3",

                    Username = "yak",

                    Password = "yakpassword",

                    Claims = new List(){new Claim(JwtClaimTypes.Role, "admin") }

                }

            };

        }

配置身份服务器

在Startup文件中配置身份服务器,加载资源和客户端,然后将测试用户注册到 IdentityServer中。代码如下:

public void ConfigureServices(IServiceCollection services)

        {

            var builder = services.AddIdentityServer()

                .AddInMemoryIdentityResources(Config.GetIdentityResources())

                .AddInMemoryApiScopes(Config.ApiScopes)

                .AddInMemoryClients(Config.GetClients())

                .AddTestUsers(Config.GetUsers());

            builder.AddDeveloperSigningCredential();

        }

 

 

AddTestUsers扩展方法在背后做了以下几件事:

(1)为资源所有者密码授权添加支持

(2)添加对用户相关服务的支持,这服务通常为登录 UI 所使用

(3)为基于测试用户的身份信息服务添加支持

 

添加客户端定义

可以通过修改AllowedGrantTypes属性简单地添加对已有客户端授权类型的支持。

通常要为资源所有者用例创建独立的客户端,添加以下代码到你配置中的客户端定义中

        public static IEnumerable GetClients()

        {

            return new List

            {

                // resource owner password grant client

                new Client

                {

                    ClientId = "yakclient",

                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                    ClientSecrets =

                    {

                        new Secret("yaksecret".Sha256())

                    },

                    AllowedScopes = { "api1", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile }//需要额外添加

                }

            };

        }

项目生成后会生成jwk加密文件,如图:

 

 

使用Postman测试工具测试

调用接口后返回access_token,测试结果如下

解密后:

新建WebAPI接口

新建接口,命名为Api,同时引用依赖:

Microsoft.AspNetCore.Authentication.JwtBearer.dll

修改接口:

注册认证相关组件和配置

Startup.cs文件中修改ConfigureServices方法,代码如下:

 public void ConfigureServices(IServiceCollection services)

        {

            services.AddControllers();

            // 注册认证相关组件和配置defaultScheme为Bearer

            services.AddAuthentication("Bearer")

              .AddJwtBearer("Bearer", options =>

              {

                  options.Authority = "http://localhost:5000";

                  // 在验证token时,不验证Audience

                  options.TokenValidationParameters = new TokenValidationParameters

                  {

                      ValidateAudience = false

                  };

                  options.RequireHttpsMetadata = false; // 不适用Https

              });

            services.AddAuthorization(options =>

            {

                options.AddPolicy("api1Scope", policy =>

                {

                    policy.RequireAuthenticatedUser();

                    policy.RequireClaim("scope", "api1");

                });

            });

        }

注意Configure方法中添加认证和授权

app.UseAuthentication();

app.UseAuthorization();

新建测试客户端

新建一个WinForm客户端,添加按钮及引用IdentityModel依赖,这里使用6.0.0版本。

按钮事件代码如下:

private void button1_Click(object sender, EventArgs e)

        {

            Task.FromResult(MainAsync());

        }

        private async Task MainAsync()

        {

            // discover endpoints from metadata

            var client = new HttpClient();

            var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");

            if (disco.IsError)

            {

                textBox1.Text = disco.Error;

                return;

            }

            textBox2.Text = disco.TokenEndpoint + "\r\n";

            // request token

            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest

            {

                Address = disco.TokenEndpoint,

                ClientId = "yakclient",

                ClientSecret = "yaksecret",

                UserName = "yak",

                Password = "yakpassword",

                Scope = "api1"

            });

            if (tokenResponse.IsError)

            {

                textBox1.Text = tokenResponse.Error;

                return;

            }

            textBox2.Text += tokenResponse.Json.ToString();

            // call api

            var apiClient = new HttpClient();

            apiClient.SetBearerToken(tokenResponse.AccessToken);

            var response = await apiClient.GetAsync("http://localhost:4571/weatherforecast");

            if (!response.IsSuccessStatusCode)

            {

                textBox1.Text += response.StatusCode.ToString();

            }

            else

            {

                var content = response.Content.ReadAsStringAsync().Result;

                textBox1.Text += content;

            }

        }

启动程序

启动多个程序项目,注意IdentityServer_ResourceOwnerPasswordCredentials项目先启动。

测试结果如下

使用Postman测试如下:

总结

本想写简单点,可是不知不觉写了好多,因为在做的过程中可能很小的细节就可能导致项目运行不了,为了方便大家了解,还是尽可能详细吧!读者照着步骤做,印象会更深。

鸣谢

https://www.cnblogs.com/savorboard/p/aspnetcore-identity.html

相关