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

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

程序员文章站 2022-03-25 08:21:06
上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用。且为实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置打下基础(第五节介绍)。 本节内容比较多,且涉及一、二节的内容,如有不懂,可先熟悉一、二节知识。 一、新建授权 ......

上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍hybrid模式在mvc下的使用。且为实现identityserver4从数据库获取user进行验证,并对claim进行权限设置打下基础(第五节介绍)。

本节内容比较多,且涉及一、二节的内容,如有不懂,可先熟悉一、二节知识。


一、新建授权服务,命名为authserver

(1)新建web api项目,不用配置https,不进行身份验证。

设置成控制台方式运行,端口设为5000。

安装identityserver4

在config.cs类中,添加如下代码:

public class config
    {

        public static list<testuser> getusers()
        {
            return new list<testuser>
            {
                new testuser
                {
                    subjectid = "1",
                    username = "test",
                    password = "123",

                    claims = new list<claim>
                    {
                        new claim("role", "user")
                    }
                },
                new testuser
                {
                    subjectid = "2",
                    username = "admin",
                    password = "123",

                    claims = new list<claim>
                    {
                        new claim("role", "admin")
                    }
                }
            };
        }

        public static ienumerable<identityresource> getidentityresources()
        {
            return new list<identityresource>
            {
                new identityresources.openid(),
                new identityresources.profile(),
                //new identityresource("roles","role",new list<string>{ "role"})
            };
        }

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

        // clients want to access resources (aka scopes)
        public static ienumerable<client> getclients()
        {
            return new list<client>
            {
                new client
                {
                    clientid = "authserver",
                    allowedgranttypes = granttypes.clientcredentials,
                    clientsecrets =
                    {
                        new secret("secret".sha256())
                    },
                    allowedscopes = { "api1" },
                    claims= new list<claim>(){new claim("role","authserver") },
                    clientclaimsprefix = ""
                },
                // openid connect implicit flow client (mvc)
                new client
                {
                   clientid = "mvc",
                   clientname = "mvc client",
                   allowedgranttypes = granttypes.hybrid,
                   clientsecrets =
                   {
                       new secret("secret".sha256())
                   },
                   // where to redirect to after login
                   redirecturis = { "http://localhost:5002/signin-oidc" },

                   // where to redirect to after logout
                   postlogoutredirecturis = { "http://localhost:5002/signout-callback-oidc" },

                  allowedscopes = new list<string>
                  {
                    identityserverconstants.standardscopes.openid,
                    identityserverconstants.standardscopes.profile,
                    //"roles"
                  }
                }
            };
        }
}

这里identityresource映射于那些关于用户信息的scope, apiresource映射于api资源的scopes。

 

(2)打开startup.cs,在configureservices里面调用addidentityserver来把identity server注册到asp.net core的容器里面;随后我调用了adddevelopersigningcredentials方法,它会创建一个用于对token签名的临时密钥材料(但是在生产环境中应该使用可持久的密钥材料)

 public void configureservices(iservicecollection services)
        {
            services.addmvc();

            services.addidentityserver()
               .adddevelopersigningcredential()
               .addtestusers(config.getusers())
               .addinmemoryidentityresources(config.getidentityresources())
               .addinmemoryapiresources(config.getapiresources())
               .addinmemoryclients(config.getclients());
        }

(3)打开configure方法,把identityserver添加到asp.net core的管道里。

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

            app.useidentityserver();
            //mvc配置
            app.usestaticfiles();
            app.usemvcwithdefaultroute();
        }

(4)然后下载登录用的ui: https://github.com/identityserver/identityserver4.quickstart.ui

把图中三个文件复制到authserver项目目录下。

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

复制完后项目如下:从Client应用场景介绍IdentityServer4(四)


 

二、新建mvc客户端,命名为mvcclient

(1)设置端口为5002。

修改start.cs的configureservices方法为:

public void configureservices(iservicecollection services)
        {
            services.configure<cookiepolicyoptions>(options =>
            {
                // this lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.checkconsentneeded = context => true;
                options.minimumsamesitepolicy = samesitemode.none;
            });


            services.addmvc();

            jwtsecuritytokenhandler.defaultinboundclaimtypemap.clear();

            services.addauthentication(options =>
            {
                options.defaultscheme = "cookies";
                options.defaultchallengescheme = "oidc";
            })
            .addcookie("cookies", options => 
            {
                //无权限,显示的页面
                options.accessdeniedpath = "/authorization/accessdenied";
            })
            .addopenidconnect("oidc", options =>
            {
                options.signinscheme = "cookies";

                options.authority = "http://localhost:5000";
                options.requirehttpsmetadata = false;

                options.clientid = "mvc";
                options.responsetype = "code id_token";
                options.scope.clear();
                options.scope.add("openid");
                options.scope.add("profile");
                //options.scope.add("roles");

                options.savetokens = true;
                options.clientsecret = "secret";
                options.getclaimsfromuserinfoendpoint = true;

                //options.claimactions.mapuniquejsonkey("role", "role");


                //options.tokenvalidationparameters = new tokenvalidationparameters
                //{
                //    nameclaimtype = jwtclaimtypes.givenname,
                //    roleclaimtype = jwtclaimtypes.role
                //};
            });
       }

addauthentication方法来添加和配置身份认证中间件。这里使用cookie作为验证用户的首选方式,而defaultscheme = "cookies",这个"cookies"字符串是可以任意填写的,只要与后边的一致即可。但是如果同一个服务器上有很多应用的话,这个scheme的名字不能重复。

defaultchanllangescheme设为"oidc", 这个名字与后边配置openidconnect的名字要一样. 当用户需要登陆的时候, 将使用的是openid connect scheme。

addcookie其参数是之前配置的defaultscheme名称,这配置了cookie的处理者,并让应用程序为我们的defaultscheme启用了基于cookie的身份认证。一旦id token验证成功并且转化为claims身份标识后,这些信息就将会保存于被加密的cookie里。

addopenidconnect方法添加了对openid connect流程的支持,它让配置了用来执行openid connect 协议的处理者。这个处理者会负责创建身份认证请求,token请求和其它请求,并负责id token的验证工作。它的身份认证scheme就是之前配置的"oidc",它的意思就是如果该客户端的某部分要求身份认证的时候,openid connect将会作为默认方案被触发(因为之前设置的defaultchallengescheme是"oidc", 和这里的名字一样)。

signinscheme和上面的defaultscheme一致,它保证身份认证成功的结果将会被保存在方案名为"cookies"的cookie里。

authority就是identity provider的地址。

clientidsecret要与identityprovider里面的值一样。

请求的scope有openid和profile,其实中间件默认也包括了这些scope,但是写出来更明确一些。

savetokens=true,表示允许存储从identity provider那里获得的tokens。

 

(2)修改configure方法为:

 public void configure(iapplicationbuilder app, ihostingenvironment env)
        {
            if (env.isdevelopment())
            {
                app.usedeveloperexceptionpage();
            }
            else
            {
                app.useexceptionhandler("/home/error");
            }

            app.useauthentication();

            app.usestaticfiles();
            app.usemvcwithdefaultroute();
        }

(3)然后对homecontroller加上身份验证。[authorize]

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

(4)再修改about的页面,显示user的claim信息。

@{
    viewdata["title"] = "about";
}
<h2>@viewdata["title"]</h2>

@*<dt>access token</dt>
<dd>@viewdata["accesstoken"]</dd>*@

<dl>
    @foreach (var claim in user.claims)
    {
        <dt>@claim.type</dt>
        <dd>@claim.value</dd>
    }
</dl>

 

(5)现在,可以运行authserver和mvcclient项目了。

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

(6)输入config文件中的testuser的用户,密码都设为123,点击login

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

允许授权从Client应用场景介绍IdentityServer4(四)

查看about页面,显示了user相关的claim信息。

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

(7)当然,登出功能还没实现,这里先实现登出。打开图中cshtml文件

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

添加如下代码:

 @if (user.identity.isauthenticated)
 {
     <li><a asp-area="" asp-controller="home" asp-action="logout">logout</a></li>
 }

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

然后在homecontroller控制器中添加logout方法

  public async task logout()

        {

            await httpcontext.signoutasync("cookies");

            await httpcontext.signoutasync("oidc");

        }

首先要清除本地的cookie,这个cookie的名字要与之前配置的默认方案里的名字一致,这一步就相当于登出mvc客户端。

后一行代码的作用是跳转回到identity provider,然后用户可以继续登出idp, 也就是idp会清除它的cookie。

(8)接着在authserver中的quickstart/account/accountoptions实现自动跳转回登录页面。

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

好了,登录登出实现完了,我们接着实现claim权限限制。


 

三、为mvc客户端设置claim身份验证

(1)添加testuser的claim中type为role

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

(2)定义用户信息scope的role信息

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

第一个参数是scope的名字,第二个参数是scope的显示名,第三个参数是它所包含的claim类型,这里就是“role”。

(3)然后还需要客户端允许请求“roles”这个scope

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

 

(4)mvc客户端的配置,打开mvc的startup,添加“roles”这个scope:options.scope.add("roles");

 把role claim 映射到user.claims里:options.claimactions.mapuniquejsonkey("role", "role");

 role claim映射成asp.net core mvc可以识别的角色roles。

options.tokenvalidationparameters = new tokenvalidationparameters
{
    nameclaimtype = jwtclaimtypes.givenname,
    roleclaimtype = jwtclaimtypes.role
};

这样mvc中的role就可以识别user.claims的role了。

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

(6)最后在mvcclient项目homecontroller中   about前,加上role为admin身份验证。[authorize(roles ="admin")]

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

然后运行,先用test账号登录进行验证。

发现点about页面没有权限进不去

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

然后登出,换admin账号登录

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

user.claims的role成功被mvc中角色role识别,展示about页面。

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


 

这节主要介绍hybrid在mvc下的使用,包括user的登录登出和claim对mvc的身份授权。

然而,这只是针对内存用户testuser进行操作的,显示实际项目中不能满足我们需求。下节将在本节的基础上介绍如何实现identityserver4从数据库获取user进行验证并对claim进行身份验证。

参考博客: 

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