identity4 系列————启航篇[二]
前言
开始identity的介绍了。
正文
前文介绍了一些概念,如果概念不清的话,可以去前文查看。
https://www.cnblogs.com/aoximin/p/13475444.html 对一些理论概念的初步介绍一下。
那么什么是identityserver4 呢?
IdentityServer is a free, open source OpenID Connect and OAuth 2.0 framework for ASP.NET Core.
Founded and maintained by Dominick Baier and Brock Allen, IdentityServer4 incorporates all the protocol implementations and extensibility points needed to integrate token-based authentication, single-sign-on and API access control in your applications. IdentityServer4 is officially certified by the OpenID Foundation and thus spec-compliant and interoperable. It is part of the .NET Foundation, and operates under their code of conduct. It is licensed under Apache 2 (an OSI approved license).
上面说identityserver 是免费开源的的OpenId Connect 和 oauth 2.0 框架。
这里有人可能会有疑问openid connect 不是基于oauth 2.0吗,这个是的呢。 只是openId connect 基于oauth 2.0 是为了实现身份相关的,而oauth 2.0 是授权其实和身份没有关系。有些不需要身份的,依然可以使用identityservice 提供的oauth 2.0的实现。
简单点说就是identityservice 实现了oauth 2.0,在oauth 2.0 的基础上又实现了openid connect,你还可以利用oauth 2.0 实现其他的。
下面也介绍了他们两个的关系:
OpenID Connect and OAuth 2.0 are very similar – in fact OpenID Connect is an extension on top of OAuth 2.0. The two fundamental security concerns, authentication and API access, are combined into a single protocol - often with a single round trip to the security token service.
We believe that the combination of OpenID Connect and OAuth 2.0 is the best approach to secure modern applications for the foreseeable future. IdentityServer4 is an implementation of these two protocols and is highly optimized to solve the typical security problems of today’s mobile, native and web applications.
现在就来实践一下吧。
学习一个框架,先看下他的quick start 吧。
https://identityserver4.readthedocs.io/en/latest/quickstarts/0_overview.html
这里有个preparation 让我们先安装模板。
dotnet new -i IdentityServer4.Templates

可以看到这样模板就安装完成了。
然后随便找一个目录,进行通过模板创建。
先创建一个空的吧。
dotnet new is4empty -n IdentityServer

官方也介绍了这几个文件是啥了。
IdentityServer.csproj - the project file and a Properties\launchSettings.json file
Program.cs and Startup.cs - the main application entry point
Config.cs - IdentityServer resources and clients configuration file
使用identity 也很简单了。
第一步就是注入服务:
var builder = services.AddIdentityServer(options =>
{
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId()
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{ };
public static IEnumerable<Client> Clients =>
new Client[]
{ };
}
第二步就是注入中间件:
app.UseIdentityServer();
这里就不直接看源码了,identity4的源码篇在后面,后面会有identity4的源码篇, 后面的更新速度也会加快。
那么来看看这几个配置是什么吧。
第一个是identityResource, 这个是什么呢? 这个是用户的身份资源。

比如邮箱地址,OpenId、 地址啊、电话号码头像之类的。
这些是用户的身份资源,表示用户的一些信息。
然后apiscopes 是什么东西呢?
An API is a resource in your system that you want to protect. Resource definitions can be loaded in many ways, the template you used to create the project above shows how to use a “code as configuration” approach.
The Config.cs is already created for you. Open it, update the code to look like this:
public static class Config
{
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
}
官方也介绍了这个是干什么的, 这个就是受保护的api 资源。
为什么要创建这个呢?
If you will be using this in production it is important to give your API a logical name.
Developers will be using this to connect to your api though your Identity server.
It should describe your api in simple terms to both developers and users.
上面是这个api的作用。
最后一个介绍的是客户端, 这里的客户端并不是我们提到的前端。
我们知道identityServer 表示的是身份服务,那么和其对接需要身份认证授权的就是客户端了。
这一点需要理解,而不是说客户端就是用户展示层,客户端和服务端都是相对而言的。
客户端的注册方式如下:
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
这样我们就注册了一个客户端, 这个客户端获取到access_token 能获得的授权是api1。
当我们配置好这些,启动程序后,我们将可以访问一个url:
https://localhost:5001/.well-known/openid-configuration
里面是什么东西呢?我们指定well-known 的意思是众所周知的意思,那么这个应该是ids 公开的信息,看一下。

里面是ids 的一些配置,比如说grant_types_supported 支持哪些授权方式。
scopes_supported 表示支持哪些资源的授权。
claims_supported 表示的是支持哪些用户身份信息。
我修改配置后:
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Address()
};
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
那么我打开配置后是这样的。

好了,这里ids 就配置完成了,也就是identityServer 就配置完了。
那么上面我们注册了受保护的api。
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
那么我们如果建立一个api1,那么我们该怎么写呢?
官网给了我们例子,那么可以看一下吧。
https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Quickstarts/1_ClientCredentials
其实就是我们的api1, 这个怎么写的问题。

打开项目后,看到了这个Api,这个就是官方给了我们例子。
其实也是很简单的, 在Startup中, 看下怎么配置的。
// accepts any access token issued by identity server
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
// adds an authorization policy to make sure the token is for scope 'api1'
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api1");
});
});
上面配置了access_token的验证方式。
然后增加了授权策略,scope 里面必须有api1。
最后再管道中注入策略, 这样就不用每个api 都加上策略了。
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization("ApiScope");
});
授权策略其实就是验证scope 中带有api1。
那么现在就知道了,apiResouces 是什么了, 且知道了api 怎么进行验证了。
那么看下客户端吧。
前文我们在ids注册了客户端为:
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
那么我们怎么请求token呢?
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
这里面使用了IdentityModel, 里面为我们封装了对identityService的请求。
上面client.GetDiscoveryDocumentAsync("https://localhost:5001"); 其实就是请求:
https://localhost:5001/.well-known/openid-configuration
通过这个获取到TokenEndpoint。

然后通过tokenEndpoint 获取到token。
里面传的也就是我们在identityServer 里面注册的客户端信息了。
来看一下获取的access_token 是怎么样的。

"eyJhbGciOiJSUzI1NiIsImtpZCI6IkM3OTEyQjMyMTREMTc1MTg3NzgyNDc1ODk4OEY1MjUxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NjA0NjIyMjgsImV4cCI6MTY2MDQ2NTgyOCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImNsaWVudF9pZCI6ImNsaWVudCIsImp0aSI6IkMzRjEyMTQwMTdGQTRCRjMyNTQ1QzJEQTA1MTlFREVDIiwiaWF0IjoxNjYwNDYyMjI4LCJzY29wZSI6WyJhcGkxIl19.u_KqoiIsdu33Tl79CbMLeVSM3Avr2tRqeyrFe62_oPlCjD34AzIa0JI1afhneRxkZ4iftENlYapSzoa2QPfvc8G4AF4HyZN-MS1CjU2nNT_ASxg84nXWUR9k6e8D7PIc-gBlPNqje-zObJjiEb_2qDTkEgmBbx8gd8zjGrClwGXVo85yhYE2efF64ymP0FV9XE34Zi9OELtVL41xGYghw-BKEL-NAXFJGBqlkmqZ-EPccsNlaBq-EG0OwqTIVTg7JNGd5LRjLLIFmY79bsns5v02qJGCVluOF8dEKCG_sRqbw7VvyBC5vP5xUElx10TsPDCn9TxyAeMGObLYninnvQ"
然后通过这个token,我们用jwt解析一下。

可以看到这个token的访问区域就是api1了。
然后通过这个token 去访问api1吧。
api1 中有一个action为:
[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
访问方式为:
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("https://localhost:6001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
查看结果:

上面介绍了一个quick start。 但是仅仅这个是不满足我们的业务需求的,我们还需要实现单点登录。下一章介绍。
结
简单整理一下identityServer4的使用,后面把那几个例子解释一下,然后就介绍实际开发中的问题,包括数据迁移、无法访问等、跳转慢等等等问题。
identity4 系列————启航篇[二]的更多相关文章
- identity4 系列————案例篇[三]
前言 前文介绍了identity的用法,同时介绍了什么是identitySourece.apiSource.client 这几个概念,和具体案例,那么下面继续介绍案例了. 正文 这里用官网的案例,因为 ...
- Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)
这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...
- SQL Server调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)
前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块 ...
- 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- SQL Server 调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)
前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块 ...
- 源码学习系列之SpringBoot自动配置(篇二)
源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...
- SpringBoot系列之集成logback实现日志打印(篇二)
SpringBoot系列之集成logback实现日志打印(篇二) 基于上篇博客SpringBoot系列之集成logback实现日志打印(篇一)之后,再写一篇博客进行补充 logback是一款开源的日志 ...
- SpringBoot系列之profles配置多环境(篇二)
SpringBoot系列之profles配置多环境(篇二) 继续上篇博客SpringBoot系列之profles配置多环境(篇一)之后,继续写一篇博客进行补充 写Spring项目时,在测试环境是一套数 ...
- 《手把手教你》系列技巧篇(二十三)-java+ selenium自动化测试-webdriver处理浏览器多窗口切换下卷(详细教程)
1.简介 上一篇讲解和分享了如何获取浏览器窗口的句柄,那么今天这一篇就是讲解获取后我们要做什么,就是利用获取的句柄进行浏览器窗口的切换来分别定位不同页面中的元素进行操作. 2.为什么要切换窗口? Se ...
随机推荐
- TKE qGPU 通过 CRD 管理集群 GPU 卡资源
作者 刘旭,腾讯云高级工程师,专注容器云原生领域,有多年大规模 Kubernetes 集群管理经验,现负责腾讯云 GPU 容器的研发工作. 背景 目前 TKE 已提供基于 qGPU 的算力/显存强隔离 ...
- c# 简单的滑动图片验证
普通的验证码对用户使用体验不友好,出现了滑动图片验证的验证方式,用户只要按住滑块完成图片的拼接即可通过验证(这是最简单的方式,滑动轨迹,数据分析,滑行速度 什么的暂没考虑) 主要的实现思路: 1.先从 ...
- 面试突击54:MySQL 常用引擎有哪些?
MySQL 有很多存储引擎(也叫数据引擎),所谓的存储引擎是指用于存储.处理和保护数据的核心服务.也就是存储引擎是数据库的底层软件组织.在 MySQL 中可以使用"show engines& ...
- 我所使用的生产 Java 17 启动参数
JVM 参数升级提示工具:jacoline.dev/inspect JVM 参数词典:chriswhocodes.com Revolut(英国支付巨头)升级 Java 17 实战:https://ww ...
- TypeScript 泛型(generic) 入门介绍
TypeScript 泛型函数 下面来创建第一个使用泛型的例子:identity函数.这个函数会返回任何传入它的值.你可以把这个函数当成是echo命令.不用泛型的话,这个函数可能是下面这样: func ...
- hadoop MapReduce运营商案例关于用户基站停留数据统计
注 如果需要文件和代码的话可评论区留言邮箱,我给你发源代码 本文来自博客园,作者:Arway,转载请注明原文链接:https://www.cnblogs.com/cenjw/p/hadoop-mapR ...
- dubbox、zookeeper BUG记录
主要错误信息: dubbo:com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method... Caused by: com.alib ...
- UiPath存在元素Element Exists的介绍和使用
一.Element Exists的介绍 使您能够验证UI元素是否存在,即使它不可见,输出的是一个布尔值 二.Element Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Se ...
- java单链表基本操作
/** * */ package cn.com.wwh; /** * @Description:TODO * @author:wwh * @time:2021-1-18 19:24:47 */ pub ...
- Java中Double类型数据比较大小
方法一:转成字符串之后比较 如果要比较的两个double数据的字符串精度相等,可以将数据转换成string然后借助string的equals方法来间接实现比较两个double数据是否相等.注意这种方法 ...