从Client应用场景介绍IdentityServer4(二)
原文:从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(二)的更多相关文章
- 从Client应用场景介绍IdentityServer4(五)
原文:从Client应用场景介绍IdentityServer4(五) 本节将在第四节基础上介绍如何实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置. 一.新建 ...
- 从Client应用场景介绍IdentityServer4(四)
原文:从Client应用场景介绍IdentityServer4(四) 上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用.且为实现IdentityServe ...
- 从Client应用场景介绍IdentityServer4(一)
原文:从Client应用场景介绍IdentityServer4(一) 一.背景 IdentityServer4的介绍将不再叙述,百度下可以找到,且官网的快速入门例子也有翻译的版本.这里主要从Clien ...
- 从Client应用场景介绍IdentityServer4(三)
原文:从Client应用场景介绍IdentityServer4(三) 在学习其他应用场景前,需要了解几个客户端的授权模式.首先了解下本节使用的几个名词 Resource Owner:资源拥有者,文中称 ...
- 消息中间件activemq的使用场景介绍(结合springboot的示例)
一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...
- Redis 中 5 种数据结构的使用场景介绍
这篇文章主要介绍了Redis中5种数据结构的使用场景介绍,本文对Redis中的5种数据类型String.Hash.List.Set.Sorted Set做了讲解,需要的朋友可以参考下 一.redis ...
- 高效而稳定的企业级.NET Office 组件Spire(.NET组件介绍之二)
在项目开发中,尤其是企业的业务系统中,对文档的操作是非常多的,有时几乎给人一种错觉的是”这个系统似乎就是专门操作文档的“.毕竟现在的很多办公中大都是在PC端操作文档等软件,在这些庞大而繁重的业务中,单 ...
- cWeb开发框架,基于asp.net的cWeb应用开发平台介绍(二)
cWeb是基于微软的.Net Framework 4框架,数据库是sql server 2008 r2. cWeb开发框架下载,点击这里去下载. cWeb开发框架借鉴三层架构理论分为三层,分别是:cD ...
- GCD介绍(二): 多核心的性能
GCD介绍(二): 多核心的性能 概念 为了在单一进程中充分发挥多核的优势,我们有必要使用多线程技术(我们没必要去提多进程,这玩意儿和GCD没关系).在低层,GCD全局dispatc ...
随机推荐
- 如何把传统写法改成框架形式 es6
每天思考的问题: 1.什么是组件 2.什么是插件 3.如何把传统写法改成框架形式 4.前端为什么要使用框架,使用框架的好处是什么? Image.png http://www.zhihu.com/que ...
- 洛谷—— P1091 合唱队形
https://www.luogu.org/problem/show?pid=1091#sub || http://codevs.cn/problem/1058/ 题目描述 N位同学站成一排,音乐 ...
- Qt开发程序在Windows 10应用须要管理员执行的解决思路
Qt开发程序在Windows 10应用须要管理员执行的解决思路 过了非常长的时间没有公布博客了.可是我依旧努力地开发Qt程序.眼下呢.我发现开发Qt程序在Windows 10上有一个怪现象--有些程序 ...
- LDD3之并发和竞态-completion(完毕量)的学习和验证
LDD3之并发和竞态-completion(完毕量)的学习和验证 首先说下測试环境: Linux2.6.32.2 Mini2440开发板 一開始难以理解书上的书面语言,这里<linux中同步样例 ...
- 基于PHP实现一个简单的在线聊天功能(轮询ajax )
基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...
- [array] leetCode-1-Two Sum-Easy
leetCode-1-Two Sum-Easy descrition Given an array of integers, return indices of the two numbers suc ...
- ATL入门
服务端代码----------------------------------------------------------------------------------------------- ...
- Behavioral模式之Memento模式
1.意图 在不破坏封装性的前提下,捕获一个对象的内部状态.并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态. 2.别名 Token 3.动机 有时候有必要记录一个对象的内部状态.为 ...
- [React Unit Testing] React unit testing demo
import React from 'react' const Release = React.createClass({ render() { const { title, artist, outO ...
- Wow6432Node(32位程序的注册表内容都在这个节点下,也可直接使用%systemroot%\syswow64\regedit进行编辑)
64 位版本 Windows 中的注册表分为 32 位注册表项和 64 位注册表项.许多 32 位注册表项与其相应的 64 位注册表项同名,反之亦然. 64 位版本 Windows 包含的默认 64 ...