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

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

一、在Server上添加动态新增Client的API 接口。

为了方便测试,在Server服务端中先添加swagger,添加流程可参考:https://www.cnblogs.com/suxinlcq/p/6757556.html

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

控制器代码如下:


二、对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]

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

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

显示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和接口返回值

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


四、测试动态新增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表找到相关记录。需要注意的是,不能添加相同Client ID的Client。


五、在Client中添加Claim信息,并在API接口中对Claim信息进行验证。

关于Claim的介绍可以看这篇文章:http://www.cnblogs.com/stulzq/p/8726002.html

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

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

TokenClient tokenClient = new TokenClient(disco.TokenEndpoint, "superAdmin", "secret");

Ctrl+F5运行

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


六、需要注意的问题

(1)新增Client到数据库时候,这里需要接收IdentityServer4.EntityFramework.Entities.Client

而不是IdentityServer4.Models.Client,否则API接口在接收和转化Client模型的时候会报错。

(2)此外,本节介绍的Client的AllowedGrantTypes 都为 GrantTypes.ClientCredentials,相应的,客户端请求是,需要用RequestClientCredentialsAsync方法。

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


Server服务端源码地址:https://github.com/Bingjian-Zhu/Server

Client客户端源码地址:https://github.com/Bingjian-Zhu/Client

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

从Client应用场景介绍IdentityServer4(二)的更多相关文章

  1. 从Client应用场景介绍IdentityServer4(五)

    原文:从Client应用场景介绍IdentityServer4(五) 本节将在第四节基础上介绍如何实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置. 一.新建 ...

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

    原文:从Client应用场景介绍IdentityServer4(四) 上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用.且为实现IdentityServe ...

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

    原文:从Client应用场景介绍IdentityServer4(一) 一.背景 IdentityServer4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本.这里主要从Clien ...

  4. 从Client应用场景介绍IdentityServer4(三)

    原文:从Client应用场景介绍IdentityServer4(三) 在学习其他应用场景前,需要了解几个客户端的授权模式.首先了解下本节使用的几个名词 Resource Owner:资源拥有者,文中称 ...

  5. 消息中间件activemq的使用场景介绍(结合springboot的示例)

    一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...

  6. Redis 中 5 种数据结构的使用场景介绍

    这篇文章主要介绍了Redis中5种数据结构的使用场景介绍,本文对Redis中的5种数据类型String.Hash.List.Set.Sorted Set做了讲解,需要的朋友可以参考下 一.redis ...

  7. 高效而稳定的企业级.NET Office 组件Spire(.NET组件介绍之二)

    在项目开发中,尤其是企业的业务系统中,对文档的操作是非常多的,有时几乎给人一种错觉的是”这个系统似乎就是专门操作文档的“.毕竟现在的很多办公中大都是在PC端操作文档等软件,在这些庞大而繁重的业务中,单 ...

  8. cWeb开发框架,基于asp.net的cWeb应用开发平台介绍(二)

    cWeb是基于微软的.Net Framework 4框架,数据库是sql server 2008 r2. cWeb开发框架下载,点击这里去下载. cWeb开发框架借鉴三层架构理论分为三层,分别是:cD ...

  9. GCD介绍(二): 多核心的性能

    GCD介绍(二): 多核心的性能  概念         为了在单一进程中充分发挥多核的优势,我们有必要使用多线程技术(我们没必要去提多进程,这玩意儿和GCD没关系).在低层,GCD全局dispatc ...

随机推荐

  1. VMware Ubuntu安装具体过程

    不是每个程序猿都必须玩过linux,仅仅是博主认为如今的非常多server都是linux系统的,而自己属于那种前端也搞.后台也搞,对框架搭建也感兴趣,可是非常多生产上的框架和工具都是安装在server ...

  2. maven 配置Project Facets时further configuration available不出来问题

    如果下边的 further configuration available不出来 把Dynamic web module 去掉勾选,应用与项目,然后再点开项目的properties,再选中Dynami ...

  3. 嵌入式Linux学习笔记 NAND Flash控制器

    一.NAND Flash介绍和NAND Flash控制器的使用 NAND Flash在嵌入式系统中的作用,相当于PC上的硬盘 常见的Flash有NOR Flash和NAND Flash,NOR Fla ...

  4. 安装GDB和GDBSERVER

    安装GDB和GDBSERVER 转自http://www.360doc.com/content/10/0407/17/155970_21971613.shtml 把GDBSERVER装入文件系统 转自 ...

  5. ORACEL上传BLOB,深度遍历文件夹

    // uploadingDlg.cpp : 实现文件// #include "stdafx.h"#include "uploading.h"#include & ...

  6. LDR: LdrpWalkImportDescriptor() failed to probe C:\WINDOWS\system32\opencv_core243d.dll for its manifest, ntstatus 0xc0150002

    LDR: LdrpWalkImportDescriptor() failed to probe C:\WINDOWS\system32\opencv_core243d.dll for its mani ...

  7. java 封装解析 Json数据。

    import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; im ...

  8. javascript怎么获取指定url网页中的内容

    javascript怎么获取指定url网页中的内容 一.总结 一句话总结:推荐jquery中ajax,简单方便. 1.js能跨域操作么? javascript出于安全机制不允许跨域操作的. 二.用ph ...

  9. markdown + vim

    https://www.jianshu.com/p/24aefcd4ca93https://github.com/isnowfy/python-vim-instant-markdownhttps:// ...

  10. ios开发知识点补充

    一:self class,self superClass  super class  super superClass 的区别 新建SubPerson继承person,在SubPerson中打印如下: ...