前言

开始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 系列————启航篇[二]的更多相关文章

  1. identity4 系列————案例篇[三]

    前言 前文介绍了identity的用法,同时介绍了什么是identitySourece.apiSource.client 这几个概念,和具体案例,那么下面继续介绍案例了. 正文 这里用官网的案例,因为 ...

  2. Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例)

    这是一个Maven提高篇的系列,包含有以下文章: Maven提高篇系列之(一)——多模块 vs 继承 Maven提高篇系列之(二)——配置Plugin到某个Phase(以Selenium集成测试为例) ...

  3. SQL Server调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)

    前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块 ...

  4. 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  5. SQL Server 调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)

    前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块 ...

  6. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  7. SpringBoot系列之集成logback实现日志打印(篇二)

    SpringBoot系列之集成logback实现日志打印(篇二) 基于上篇博客SpringBoot系列之集成logback实现日志打印(篇一)之后,再写一篇博客进行补充 logback是一款开源的日志 ...

  8. SpringBoot系列之profles配置多环境(篇二)

    SpringBoot系列之profles配置多环境(篇二) 继续上篇博客SpringBoot系列之profles配置多环境(篇一)之后,继续写一篇博客进行补充 写Spring项目时,在测试环境是一套数 ...

  9. 《手把手教你》系列技巧篇(二十三)-java+ selenium自动化测试-webdriver处理浏览器多窗口切换下卷(详细教程)

    1.简介 上一篇讲解和分享了如何获取浏览器窗口的句柄,那么今天这一篇就是讲解获取后我们要做什么,就是利用获取的句柄进行浏览器窗口的切换来分别定位不同页面中的元素进行操作. 2.为什么要切换窗口? Se ...

随机推荐

  1. 从零开始实现lmax-Disruptor队列(一)RingBuffer与单生产者、单消费者工作原理解析

    1.lmax-Disruptor队列介绍 disruptor是英国著名的金融交易所lmax旗下技术团队开发的一款java实现的高性能内存队列框架 其发明disruptor的主要目的是为了改进传统的内存 ...

  2. Sublime text eslint windows 配置

    1. 下载安装eslint npm install -g eslint 2. 设置环境变量 C:\Users\<你的用户名>\AppData\Roaming\npm 3. sublime ...

  3. SAP SD-Invoice 销售发票

    针对销售订单的发票流程: 1. 事务码:VF01(个别生成系统发票) 创建开票凭证(发票)/  VF04 开具系统发票(可把多个item 合并成一张系统发票) 2. 事务码:VF02 修改发票, 释放 ...

  4. SE37 绕过权限检查 ALINK_CALL_TRANSACTION

  5. 隐私计算FATE-离线预测

    一.说明 Fate 的模型预测有 离线预测 和 在线预测 两种方式,两者的效果是一样的,主要是使用方式.适用场景.高可用.性能等方面有很大差别:本文分享使用 Fate 基于 纵向逻辑回归 算法训练出来 ...

  6. Linux shell脚本基础

    程序的组成: 程序:算法+数据结构 数据:程序处理的目标 数据结构:相互之间存在一种或多种特定关系的数据元素的集合 算法:处理数据的方式 编程风格: 面向对象:把所有的操作都转化为对象的方式. 面向过 ...

  7. UML图记忆技巧

    什么是UML类图 Class Diagram:用于表示类.接口.实例等之间相互的静态关系 虽然名字叫类图,但类图中并不只有类 记忆技巧 UML箭头方向: 从子类指向父类 我相信 很多同学都会有和我一样 ...

  8. 讲给测试人员的docker知识

    docker对测试来说有什么用 docker类似于Windows系统的虚拟机,对于测试来说docker意味着一种新的测试环境部署方式,由于其镜像分层的设置,我们可以在一台物理机上同过docker的方式 ...

  9. 函数式(Functional)接口

    public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { ...

  10. Ajax:异步的JS和XML

    1.Ajax1) AJAX 是 Asynchronous JavaScript And XML 的简称.直译为,异步的JS和XML.2) AJAX的实际意义是,不发生页面跳转.异步载入内容并改写页面内 ...