IdentityServer4-EF動態配置Client和對Claims受權(二)

本節介紹Client的ClientCredentials客戶端模式,先看下畫的草圖:html

1、在Server上添加動態新增Client的API 接口。

爲了方便測試,在Server服務端中先添加swagger,添加流程可參考:http://www.javashuo.com/article/p-dmhwqpja-ds.htmlgit

 

在ValuesController控制器中注入ConfigurationDbContext上下文,此上下文可用來加載或配置IdentityServer4.EntityFramework的Client、身份信息、API資源信息或CORS數據等。github

在ValuesController中實添加如下代碼:數據庫

        private ConfigurationDbContext _context;
        public ValuesController(ConfigurationDbContext context)
        {
            _context = context;
        }

添加動態新增Client的API接口:json

        [HttpPost]
        public IActionResult Post([FromBody] IdentityServer4.EntityFramework.Entities.Client client)
        {
            var res = _context.Clients.Add(client);
            if(_context.SaveChanges() >0)
                return Ok(true);
            else
                return Ok(false);
        }

控制器代碼以下:api


 

2、對Server上的API進行保護

(1)安裝IdentityServer4.AccessTokenValidation包瀏覽器

(2)在startup.cs中ConfigureServices方法添加以下代碼:app

            //protect API
            services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters();

            services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;

                    options.ApiName = "api1";
                });

AddAuthentication把Bearer配置成默認模式,將身份認證服務添加到DI中。async

AddIdentityServerAuthentication把IdentityServer的access token添加到DI中,供身份認證服務使用。post

(3)在startup.cs中Configure方法添加以下代碼:

      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //if (env.IsDevelopment())
            //{
            //    app.UseDeveloperExceptionPage();
            //}

            //AddSwagger
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Server接口文檔");
            });

            InitializeDatabase(app);
            app.UseAuthentication();
            app.UseIdentityServer();
            app.UseMvc();
        }

UseAuthentication將身份驗證中間件添加到管道中,以便在每次調用主機時自動執行身份驗證。

(4)在ValuesController控制器中添加[Authorize]

 

(5)在項目屬性->調試 中,啓動瀏覽器,並設成swagger,如圖:

(6)啓動項目,並調用第一個Get接口。

顯示Unauthorized(未受權),證實[Authorize]起做用了。


 

3、搭建Client客戶端

(1)新建一個控制檯程序,安裝IdentityModel包

(2)添加類IDSHelper.cs,添加客戶端請求API接口代碼。

public class IDSHelper
    {
        public static async Task MainAsync()
        {
            try
            {
                DiscoveryResponse disco = await DiscoveryClient.GetAsync("http://localhost:5000");
                if (disco.IsError)
                {
                    Console.WriteLine(disco.Error);
                    return;
                }

                TokenClient tokenClient = new TokenClient(disco.TokenEndpoint, "Client", "secret");
                var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

                if (tokenResponse.IsError)
                {
                    Console.WriteLine(tokenResponse.Error);
                    return;
                }
                Console.WriteLine(tokenResponse.Json);
                var client = new HttpClient();
                client.SetBearerToken(tokenResponse.AccessToken);
                var response = await client.GetAsync("http://localhost:5000/api/values/");
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine(response.StatusCode);
                }
                else
                {
                    var content = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(content);
                }
            }
            catch (Exception ex)
            {

            }
        }
}

(3)修改Program.cs代碼,以下:

class Program
    {
        static void Main(string[] args)
       => IDSHelper.MainAsync().GetAwaiter().GetResult();
    }

(4)按Ctrl+F5,能夠獲取到access token和接口返回值

複製token,用postman調用,成功獲取到了接口返回值。


 

4、測試動態新增Client接口

安裝IdentityServer4包。

安裝IdentityServer4.EntityFramework包。

在IDSHelper.cs類中添加Post方法:

public static async Task Post()
        {
            try
            {
                DiscoveryResponse disco = await DiscoveryClient.GetAsync("http://localhost:5000");
                if (disco.IsError)
                {
                    Console.WriteLine(disco.Error);
                    return;
                }

                TokenClient tokenClient = new TokenClient(disco.TokenEndpoint, "Client", "secret");
                var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

                if (tokenResponse.IsError)
                {
                    Console.WriteLine(tokenResponse.Error);
                    return;
                }
                Console.WriteLine(tokenResponse.Json);
                var client = new HttpClient();
                client.SetBearerToken(tokenResponse.AccessToken);

                Client c1 = new Client
                {
                    ClientId = "Test",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "api1" }
                };
                string strJson = JsonConvert.SerializeObject(c1 .ToEntity());
                HttpContent content = new StringContent(strJson);
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                //由HttpClient發出Post請求
                Task<HttpResponseMessage> response = client.PostAsync("http://localhost:5000/api/values/", content);

                if (response.Result.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    Console.WriteLine(response.Result.StatusCode);
                }
                else
                {
                    Console.WriteLine(response.Result.Content.ReadAsStringAsync().Result);
                }
            }
            catch (Exception ex)
            {

            }
        }

順便把main中改爲對Post調用:

static void Main(string[] args)

       => IDSHelper.Post().GetAwaiter().GetResult();

按Ctrl+F5,調用新增Client的接口,併成功返回true。

同時能夠在數據庫中的Client表找到相關記錄。須要注意的是,不能添加相同Client ID的Client。


 

5、在Client中添加Claim信息,並在API接口中對Claim信息進行驗證。

關於Claim的介紹能夠看這篇文章:http://www.cnblogs.com/stulzq/p/8726002.html

這裏把Claim簡單當作用戶的身份信息使用,修改Post方法裏面的Client:

                Client c1 = new Client
                {
                    ClientId = "superAdmin",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "api1" },
                    Claims = new List<Claim>
                    {
                        new Claim(JwtClaimTypes.Role, "admin")
                    }
                };

能夠看出,Claims爲List,能夠是不少個角色,這裏只添加一個。

Ctrl+F5,運行成功添加superAdmin Client。

 

如今,須要對Server服務端的新增Client接口進行Claim身份驗證,添加以下代碼:

 [Authorize(Roles ="admin")]

 

而後再客戶端修改受權的帳號爲superadmin。

TokenClient tokenClient = new TokenClient(disco.TokenEndpoint, "superAdmin", "secret");

Ctrl+F5運行

問題出現了,返回了Forbidden,沒有權限進行訪問。

       這時候咱們上官網查閱了資料,發如今添加Client的Claim時候,IdentityServer EntityFramework會爲Claim的role添加一個默認前綴,爲client_。因此,實際上它爲client_role

而服務端只能對role進行驗證。

此時咱們須要把Claim的默認前綴去掉,設置爲空ClientClaimsPrefix = ""

 

去掉Server的Role驗證,添加形以下面代碼的Client。

Client c1 = new Client
                {
                    ClientId = "adminClient",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "api1" },
                    Claims = new List<Claim>
                    {
                        new Claim(JwtClaimTypes.Role, "admin")
                    },
                    ClientClaimsPrefix = "" //把client_ 前綴去掉
                };

 Ctrl+F5,運行成功添加adminClient Client,此次的是Role爲admin。

而後從新再Server服務端加上[Authorize(Roles ="admin")]

同時修改驗證帳號爲adminClient。

TokenClient tokenClient = new TokenClient(disco.TokenEndpoint, "adminClient", "secret");

最後運行程序,成功地在[Authorize(Roles ="admin")]權限下訪問並新增了Client。


 

6、須要注意的問題

(1)新增Client到數據庫時候,這裏須要接收IdentityServer4.EntityFramework.Entities.Client

而不是IdentityServer4.Models.Client,不然API接口在接收和轉化Client模型的時候會報錯。

(2)此外,本節介紹的Client的AllowedGrantTypes 都爲 GrantTypes.ClientCredentials,相應的,客戶端請求是,須要用RequestClientCredentialsAsync方法。

最後再次提下,ClientCredentials模式的適用場景:用於和用戶無關,服務與服務之間直接交互訪問資源


 

Server服務端源碼地址:https://github.com/Bingjian-Zhu/Server

Client客戶端源碼地址:https://github.com/Bingjian-Zhu/Client

文中若有錯漏,歡迎指正。

相關文章
相關標籤/搜索