从Client应用场景介绍IdentityServer4(四)
上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍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项目目录下。
复制完后项目如下:
二、新建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的地址。
clientid和secret要与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]
(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项目了。
(6)输入config文件中的testuser的用户,密码都设为123,点击login
允许授权
查看about页面,显示了user相关的claim信息。
(7)当然,登出功能还没实现,这里先实现登出。打开图中cshtml文件
添加如下代码:
@if (user.identity.isauthenticated) { <li><a asp-area="" asp-controller="home" asp-action="logout">logout</a></li> }
然后在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实现自动跳转回登录页面。
好了,登录登出实现完了,我们接着实现claim权限限制。
三、为mvc客户端设置claim身份验证
(1)添加testuser的claim中type为role
(2)定义用户信息scope的role信息
第一个参数是scope的名字,第二个参数是scope的显示名,第三个参数是它所包含的claim类型,这里就是“role”。
(3)然后还需要客户端允许请求“roles”这个scope
(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了。
(6)最后在mvcclient项目homecontroller中 about前,加上role为admin身份验证。[authorize(roles ="admin")]
然后运行,先用test账号登录进行验证。
发现点about页面没有权限进不去
然后登出,换admin账号登录
user.claims的role成功被mvc中角色role识别,展示about页面。
这节主要介绍hybrid在mvc下的使用,包括user的登录登出和claim对mvc的身份授权。
然而,这只是针对内存用户testuser进行操作的,显示实际项目中不能满足我们需求。下节将在本节的基础上介绍如何实现identityserver4从数据库获取user进行验证并对claim进行身份验证。
参考博客: