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

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

程序员文章站 2022-04-05 11:20:59
一、背景 IdentityServer4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本。然而发现关于它应用方面的文章介绍稍微欠缺,所以这里主要从Client应用场景方面介绍对IdentityServer4的应用。 首先简要介绍ID Token和Access Token: Acc ......

一、背景

identityserver4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本。然而发现关于它应用方面的文章介绍稍微欠缺,所以这里主要从client应用场景方面介绍对identityserver4的应用。

首先简要介绍id token和access token:

access token是授权第三方客户端访问受保护资源的令牌。 id token是第三方客户端标识用户身份认证的问令牌,是json web token格式。

 


 

二、client应用场景介绍

client类是为openid connect或oauth 2.0 协议建模的。

我们先看官网快速入门中给的client例子

 public static ienumerable<client> getclients()
        {
            // client credentials client
            return new list<client>
            {
                new client
                {
                    clientid = "client",
                    allowedgranttypes = granttypes.clientcredentials,
                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },
                    allowedscopes = { "api1" }
                },

                // resource owner password grant client
                new client
                {
                    clientid = "ro.client",
                    allowedgranttypes = granttypes.resourceownerpassword,

                    clientsecrets = 
                    {
                        new secret("secret".sha256())
                    },
                    allowedscopes = { "api1" }                },

                // openid connect hybrid flow and client credentials client (mvc)
                new client
                {
                    clientid = "mvc",
                    clientname = "mvc client",
                    allowedgranttypes = granttypes.hybridandclientcredentials,

                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },

                    redirecturis = { "http://localhost:5002/signin-oidc" },
                    postlogoutredirecturis = { "http://localhost:5002/signout-callback-oidc" },

                    allowedscopes =
                    {
                        identityserverconstants.standardscopes.openid,
                        identityserverconstants.standardscopes.profile,
                        "api1"
                    },
                    allowofflineaccess = true
                },

                // javascript client
                new client
                {
                    clientid = "js",
                    clientname = "javascript client",
                    allowedgranttypes = granttypes.implicit,
                    allowaccesstokensviabrowser = true,

                    redirecturis = { "http://localhost:5003/callback.html" },
                    postlogoutredirecturis = { "http://localhost:5003/index.html" },
                    allowedcorsorigins = { "http://localhost:5003" },

                    allowedscopes =
                    {
                        identityserverconstants.standardscopes.openid,
                        identityserverconstants.standardscopes.profile,
                        "api1"
                    },
                }
            };
        }

里面主要介绍四种client应用场景。

(1)客户端授权模式(allowedgranttypes = granttypes.clientcredentials

    这是一种最简单的授权方式,应用于服务于服务之间的通信,token通常代表的是客户端的请求,而不是用户。

    使用这种授权类型,会向token endpoint发送token请求,并获得代表客户机的access token。客户端通常必须使用token endpoint的client id和secret进行身份验证。

    适用场景:用于和用户无关,机器与机器之间直接交互访问资源

(2)资源的密码授权模式(clientallowedgranttypes = granttypes.resourceownerpassword

    该方式发送用户名和密码到token endpoint,向资源服务器请求令牌。这是一种“非交互式”授权方法。

    官网上称,为了解决一些历史遗留的应用场景,所以保留了这种授权方式,但不建议使用。

    适用场景:用于当前的app是专门为服务端设计的情况。

(3)hybrid和客户端授权模式(clientallowedgranttypes =granttypes.hybridandclientcredentials

    clientcredentials授权方式在第一种应用场景已经介绍了,这里主要介绍hybrid授权方式。hybrid是由implicit和authorization code结合起来的一种授权方式。其中implicit用于身份认证,id token在浏览器传输;而authorization code使用反向通道检索token和刷新token。

    推荐适用hybrid模式。

    适用场景:用于mvc框架,服务器端 web 应用程序和原生桌面/移动应用程序。

(4)简化模式(clientallowedgranttypes =granttypes.implicit

    implicit要么仅用于服务端和javascript应用程序端进行身份认证,要么用于身份身份验证和access token的传输。

    在implicit中,所有token都通过浏览器传输的。

    适用场景:javascript应用程序。


 

三、server端搭建

为了介绍identityserver4的client应用场景,我们需要先搭建identityserver服务端。

这里搭建的是使用ef core来做数据操作,保存到sql server中。

(1)新建api项目

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

(2)安装identityserver4.entityframework包

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

(3)安装identityserver4包

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

(4)右键项目的属性,编辑项目的.csproj文件

添加如下元素

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

<itemgroup>
    <dotnetclitoolreference include="microsoft.entityframeworkcore.tools.dotnet" version="2.0.0" />
</itemgroup>

如图:

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

(5)cmd管理员身份进入项目目录路径(d:\identityserver4\server),运行:dotnet ef

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

(6)项目内添加config.cs类,代码如下

 public class config
    {
        public static list<testuser> getusers()
        {
            return new list<testuser>
            {
                new testuser
                {
                    subjectid = "1",
                    username = "alice",
                    password = "password",

                  claims = new list<claim>(){new claim(jwtclaimtypes.role,"superadmin") }
                },
                new testuser
                {
                    subjectid = "2",
                    username = "bob",
                    password = "password",

                    claims = new list<claim>
                    {
                        new claim("name", "bob"),
                        new claim("website", "https://bob.com")
                    },
                }
            };
        }

        public static ienumerable<client> getclients()
        {
            // client credentials client
            return new list<client>
            {
                new client
                {
                    clientid = "client",
                    allowedgranttypes = granttypes.clientcredentials,
                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },
                    allowedscopes = { "api1" }
                },

                // resource owner password grant client
                new client
                {
                    clientid = "ro.client",
                    allowedgranttypes = granttypes.resourceownerpassword,

                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },
                    allowedscopes = { "api1" }
                },

                // openid connect hybrid flow and client credentials client (mvc)
                new client
                {
                    clientid = "mvc",
                    clientname = "mvc client",
                    allowedgranttypes = granttypes.hybridandclientcredentials,

                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },

                    redirecturis = { "http://localhost:5002/signin-oidc" },
                    postlogoutredirecturis = { "http://localhost:5002/signout-callback-oidc" },

                    allowedscopes =
                    {
                        identityserverconstants.standardscopes.openid,
                        identityserverconstants.standardscopes.profile,
                        "api1"
                    },
                    allowofflineaccess = true
                },

                // javascript client
                new client
                {
                    clientid = "js",
                    clientname = "javascript client",
                    allowedgranttypes = granttypes.implicit,
                    allowaccesstokensviabrowser = true,

                    redirecturis = { "http://localhost:5003/callback.html" },
                    postlogoutredirecturis = { "http://localhost:5003/index.html" },
                    allowedcorsorigins = { "http://localhost:5003" },

                    allowedscopes =
                    {
                        identityserverconstants.standardscopes.openid,
                        identityserverconstants.standardscopes.profile,
                        "api1"
                    },
                }
            };
        }
        public static ienumerable<identityresource> getidentityresources()
        {
            return new list<identityresource>
            {
                new identityresources.openid(),
                new identityresources.profile(),
            };
        }

        public static ienumerable<apiresource> getapiresources()
        {
            return new list<apiresource>
            {
                new apiresource("api1", "my api")
            };
        }

添加引用:

using identitymodel;

using identityserver4;

using identityserver4.models;

using identityserver4.test;

using system.collections.generic;

using system.security.claims;

(7)编辑startup.cs文件的configureservices方法,改成如下代码。

public void configureservices(iservicecollection services)
        {
            const string connectionstring = @"server=localhost;database=identityserver4;user id=sa;password=pwd;trusted_connection=yes";
            var migrationsassembly = typeof(startup).gettypeinfo().assembly.getname().name;

            // configure identity server with in-memory stores, keys, clients and scopes
            services.addidentityserver()
                .adddevelopersigningcredential()
                .addtestusers(config.getusers())
                // this adds the config data from db (clients, resources)
                .addconfigurationstore(options =>
                {
                    options.configuredbcontext = builder =>
                        builder.usesqlserver(connectionstring,
                            sql => sql.migrationsassembly(migrationsassembly));
                })
                // this adds the operational data from db (codes, tokens, consents)
                .addoperationalstore(options =>
                {
                    options.configuredbcontext = builder =>
                        builder.usesqlserver(connectionstring,
                            sql => sql.migrationsassembly(migrationsassembly));

                    // this enables automatic token cleanup. this is optional.
                    options.enabletokencleanup = false;//是否从数据库清楚令牌数据,默认为false
                    options.tokencleanupinterval = 300;//令牌过期时间,默认为3600秒,一个小时
                });
            //.addinmemoryclients(config.getclients());
            services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1);
        }

添加引用:

using microsoft.entityframeworkcore;

using system.reflection;

(8)cmd管理员身份进入到项目目录路径(d:\identityserver4\server\server),注意,多了一层目录,分别运行以下两条指令:

dotnet ef migrations add initialidentityserverpersistedgrantdbmigration -c persistedgrantdbcontext -o data/migrations/identityserver/persistedgrantdb

dotnet ef migrations add initialidentityserverconfigurationdbmigration -c configurationdbcontext -o data/migrations/identityserver/configurationdb

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

运行完后,项目中会多了一个data文件夹

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

(9)在startup.cs中添加初始化数据库方法。

private void initializedatabase(iapplicationbuilder app)
{
    using (var servicescope = app.applicationservices.getservice<iservicescopefactory>().createscope())
    {
        servicescope.serviceprovider.getrequiredservice<persistedgrantdbcontext>().database.migrate();

        var context = servicescope.serviceprovider.getrequiredservice<configurationdbcontext>();
        context.database.migrate();
        if (!context.clients.any())
        {
            foreach (var client in config.getclients())
            {
                context.clients.add(client.toentity());
            }
            context.savechanges();
        }

        if (!context.identityresources.any())
        {
            foreach (var resource in config.getidentityresources())
            {
                context.identityresources.add(resource.toentity());
            }
            context.savechanges();
        }

        if (!context.apiresources.any())
        {
            foreach (var resource in config.getapiresources())
            {
                context.apiresources.add(resource.toentity());
            }
            context.savechanges();
        }
    }
}

添加引用:

using identityserver4.entityframework.dbcontexts;

using identityserver4.entityframework.mappers;

(10)在startup.cs中的configure方法修改成以下代码。

  public void configure(iapplicationbuilder app, ihostingenvironment env)
        {
            //if (env.isdevelopment())
            //{
            //    app.usedeveloperexceptionpage();
            //}
            initializedatabase(app);
            //app.usemvc();
        }

到这里,把项目以控制台形式运行

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

点击运行,可以跑起来,且生成数据库identityserver4db。

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

关于client的说明可以查阅官网资料:https://identityserver4.readthedocs.io/en/release/reference/client.html

源码地址:https://github.com/bingjian-zhu/server.git

服务端准备好之后,下篇文章开始介绍client客户端的应用。

文中如有错漏,欢迎指正,将对此系列文章进行维护。