从Client应用场景介绍IdentityServer4(一)
一、背景
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项目
(2)安装identityserver4.entityframework包
(3)安装identityserver4包
(4)右键项目的属性,编辑项目的.csproj文件
添加如下元素
<itemgroup> <dotnetclitoolreference include="microsoft.entityframeworkcore.tools.dotnet" version="2.0.0" /> </itemgroup>
如图:
(5)cmd管理员身份进入项目目录路径(d:\identityserver4\server),运行:dotnet ef
(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
运行完后,项目中会多了一个data文件夹
(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(); }
到这里,把项目以控制台形式运行
点击运行,可以跑起来,且生成数据库identityserver4db。
关于client的说明可以查阅官网资料:https://identityserver4.readthedocs.io/en/release/reference/client.html
源码地址:https://github.com/bingjian-zhu/server.git
服务端准备好之后,下篇文章开始介绍client客户端的应用。
文中如有错漏,欢迎指正,将对此系列文章进行维护。
推荐阅读