前言

开始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. HTML 继承属性

    一.无继承性的属性 1.display:规定元素应该生成的框的类型 2.文本属性: vertical-align:垂直文本对齐 text-decoration:规定添加到文本的装饰 text-shad ...

  2. DYOJ 【20220317模拟赛】瞬间移动 题解

    瞬间移动 题意 三维空间中从 \((0,0,0)\) 开始,每次移动 1,问刚好走 \(N\) 次能到 \((X,Y,Z)\) 的方案数 \(N\le10^7\),答案模 \(998244353\) ...

  3. 入坑KeePass(二)重置keepass设置

    保留好.kdbx和密钥文件,软件的文件可以删除掉,重新下载并解压设置就恢复默认了

  4. 开发工具-PowerShell下载地址

    更新日志 2022年6月10日 初始化链接. https://github.com/PowerShell/PowerShell/releases/

  5. 全球共有多少MySQL实例在运行?这里有一份数据

    摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例.因为这份报告基数够大,而且信息也非常完整,从数据库专业 ...

  6. 【Redis】简单动态字符串SDS

    C语言字符串 char *str = "redis"; // 可以不显式的添加\0,由编译器添加 char *str = "redis\0"; // 也可以添加 ...

  7. JS:!非

    取非运算符: 开关思想:0为false,1为true: 把一个变量中保存一个布尔值 然后在业务执行时,修改这个变量的值: 为取反 然后通过变量的值执行分支业务 例子: var a = "12 ...

  8. JS:||运算符

    ||逻辑运算符 ||这个符号在开发中 往往是优化的代码最常用的js符号: 当用||连接语句时,回将前后语句变为Boolean类型,再进行运算: 1.当||前面条件为false,不管后面是true/fa ...

  9. node zlib压缩模块了解一下

    压缩: 从index.html压缩成index.html.gz const zlib = require('zlib'); const gzip = zlib.createGzip();const f ...

  10. python基础知识-day8(函数实战)

    1 def out(): 2 username=input("请输入用户名:\n") 3 password=input("请输入密码:\n") 4 return ...