欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从Client应用场景介绍IdentityServer4(二)

程序员文章站 2022-03-24 12:21:35
本节介绍Client的ClientCredentials客户端模式,先看下画的草图: 一、在Server上添加动态新增Client的API 接口。 为了方便测试,在Server服务端中先添加swagger,添加流程可参考:https://www.cnblogs.com/suxinlcq/p/6757 ......

本节介绍client的clientcredentials客户端模式,先看下画的草图:

从Client应用场景介绍IdentityServer4(二)

一、在server上添加动态新增client的api 接口。

为了方便测试,在server服务端中先添加swagger,添加流程可参考:

 

在valuescontroller控制器中注入configurationdbcontext上下文,此上下文可用来加载或配置identityserver4.entityframework的client、身份信息、api资源信息或cors数据等。

在valuescontroller中实添加以下代码:

        private configurationdbcontext _context;
        public valuescontroller(configurationdbcontext context)
        {
            _context = context;
        }

添加动态新增client的api接口:

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

控制器代码如下:

从Client应用场景介绍IdentityServer4(二)


 

二、对server上的api进行保护

(1)安装identityserver4.accesstokenvalidation包

(2)在startup.cs中configureservices方法添加如下代码:

            //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中。

addidentityserverauthentication把identityserver的access token添加到di中,供身份认证服务使用。

(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]

从Client应用场景介绍IdentityServer4(二)

 

(5)在项目属性->调试 中,启动浏览器,并设成swagger,如图:

从Client应用场景介绍IdentityServer4(二)

(6)启动项目,并调用第一个get接口。

从Client应用场景介绍IdentityServer4(二)

显示unauthorized(未授权),证明[authorize]起作用了。


 

三、搭建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和接口返回值

从Client应用场景介绍IdentityServer4(二)

复制token,用postman调用,成功获取到了接口返回值。

从Client应用场景介绍IdentityServer4(二)


 

四、测试动态新增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应用场景介绍IdentityServer4(二)

同时可以在数据库中的client表找到相关记录。需要注意的是,不能添加相同client id的client。


 

五、在client中添加claim信息,并在api接口中对claim信息进行验证。

关于claim的介绍可以看这篇文章:

这里把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")]

从Client应用场景介绍IdentityServer4(二)

然后再客户端修改授权的账号为superadmin。

tokenclient tokenclient = new tokenclient(disco.tokenendpoint, "superadmin", "secret");

ctrl+f5运行

从Client应用场景介绍IdentityServer4(二)

问题出现了,返回了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。

从Client应用场景介绍IdentityServer4(二)


 

六、需要注意的问题

(1)新增client到数据库时候,这里需要接收identityserver4.entityframework.entities.client

而不是identityserver4.models.client,否则api接口在接收和转化client模型的时候会报错。

从Client应用场景介绍IdentityServer4(二)

(2)此外,本节介绍的client的allowedgranttypes 都为 granttypes.clientcredentials,相应的,客户端请求是,需要用requestclientcredentialsasync方法。

从Client应用场景介绍IdentityServer4(二)

最后再次提下,clientcredentials模式的适用场景:用于和用户无关,服务与服务之间直接交互访问资源


 

server服务端源码地址:https://github.com/bingjian-zhu/server

client客户端源码地址:https://github.com/bingjian-zhu/client

文中如有错漏,欢迎指正。