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 ...
随机推荐
- CF1580E Railway Construction
CF1580E Railway Construction 铁路系统中有 \(n\) 个车站和 \(m\) 条双向边,有边权,无重边.这些双向边使得任意两个车站互相可达. 你现在要加一些单向边 \((u ...
- IDEA windows版本快捷键
使用本快捷键前,可以在idea使用下面方法确认版本! Ctrl 快捷键 介绍 Ctrl + F 在当前文件进行文本查找 (必备)Ctrl + R 在当前文件进行文本替换 (必备) Ctrl + Z 撤 ...
- android系统中有哪些日志
日志目录 android系统中还有很多常用的日志目录.我们可以通过adb命令把这些日志信息提取出来. data/system/dropbox data/system/usagestats data/s ...
- sort是不稳定排序
今天才知道sort是不稳定的,WA了一个点. sort()排序不是稳定排序,sort是主要用到了快速排序(平均时间复杂度为O(nlogn)),还结合了插入排序(时间复杂度为O(n2))和堆排序(时间复 ...
- 为什么要写blog????
写 blog 文章,是种与自我的对话,也是种与外界的联系,也是获得 level up 或 skill learned 的契机. 借口:我不太会写文章,不太会表达,没有东西好写,没人会看我的文章 你想让 ...
- MongoDB学习总览
第1部分: MongoDB入门(第1~6章) 该部分介绍MongoDB的基本概念及入门知识. 通过该部分的学习,读者可对MongoDB自身的技术全貌形成一定的认识. 第2部分: MongoDB微服务开 ...
- 【Redis】事件驱动框架源码分析(多线程)
IO线程初始化 Redis在6.0版本中引入了多线程,提高IO请求处理效率. 在Redis Server启动函数main(server.c文件)中初始化服务之后,又调用了InitServerLast函 ...
- 【Spring】事务的执行原理(三)
事务的回滚 如果获取事务属性不为空,并且抛出的异常是RuntimeException或者Error类型,调用事务管理器中的rollback方法进行回滚 如果事务属性为空或者抛出的异常不是Runtime ...
- NC19115 选择颜色
NC19115 选择颜色 题目 题目描述 \(n\) 个人排成一个环形,每个人要从 \(c\) 种颜色中选择一个. 牛牛希望相邻的人选择的颜色是不同的 问有多少种方案. 输出方案数对 \(10007\ ...
- 《Stepwise Metric Promotion for Unsupervised Video Person Re-identification》 ICCV 2017
Motivation: 这是ICCV 17年做无监督视频ReID的一篇文章.这篇文章简单来说基于两个Motivation. 在不同地方或者同一地方间隔较长时间得到的tracklet往往包含的人物是不同 ...