深入了解身份认证和授权机制,看看API请求到底发生了什么?
前段时间写了一篇基于.NetCore环境使用IdentityServer4为API接口鉴权的文章,更多的是从快速上手的角度描述了IdentityServer4的使用。后续使用过程中,自己有了一些其他想法和困惑,于是便进行一番探索,在这里记录分享一下。
本文主要和大家认识下Client获取到Token之后,请求API资源时具体是如何实现身份认证和资源授权的。
解决以下困惑:
1.Client请求时携带的Token具体是如何实现认证的?
2.Token中自包含的信息(IdentityServer4使用的Token实际是JWT类型)是如何传递到API资源服务器中的?
3.为什么被标识了【Authorize】特性或者被全局设置过滤器AuthorizeFilter的资源就能实现鉴权,系统是如何实现的?
准备环境:
1.IdentityServer4授权服务
2.API资源服务
3.Postman模拟发起http请求的客户端
由于之前文章讲解过如何实现IdentityServer鉴权,本文就不再赘述如何搭建项目环境,感兴趣的同学可以看下之前的文章IdentityServer4实现.Net Core API接口权限认证
关注下代码核心部分:
API资源服务中注册认证服务
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();//注册身份认证服务
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("Authority:Url");//你要请求验证的identity服务端的地址
options.RequireHttpsMetadata = false;
options.ApiName = "api";//你选择的验证方式。 对应的GetClients中定义的作用域
}); }
1)AddAuthentication内部注册了我们最主要的三个对象AuthenticationService, AuthenticationHandlerProvider, AuthenticationSchemeProvider
2)AddIdentityServerAuthentication(或者AddCookie、AddJwtBearer)指定Scheme类型和需要验证的参数
API资源服务中添加身份认证、授权管道中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseRouting();
//添加权限认证中间件
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
1) app.UseAuthentication() 添加了中间件AuthenticationMiddleware
2) app.UseAuthorization() 添加了中间件AuthorizationMiddleware
.NetCore中Http请求过程:
Http请求 > 其它中间件 > 路由中间件(这里就拿到终点路由了) > 身份验证中间件 > 授权中间件 > MVC中间件 > Controller > Action
深入认识下身份认证和授权两个过程:
发起API接口请求(header中参数Authorization携带token)

Http请求先经过身份认证AuthenticationMiddleware中间件请求管道,然后经过授权AuthorizationMiddleware中间件请求管道。
1.身份认证 即token认证
借用一张大佬的图,给小伙伴们解解渴。

结合上面这张图,我们来详细看看这其中到底是如何实现的?
API服务一共向IdentityServer4授权服务请求了两次
1)第一次请求IdentityServer4授权服务,指向终点路由DiscoveryEndPoint(/.well-known/openid-configuration),获取IdentityServer授权服务配置信息
{
"issuer": "http://localhost:7000",
"jwks_uri": "http://localhost:7000/.well-known/openid-configuration/jwks",
"authorization_endpoint": "http://localhost:7000/connect/authorize",
"token_endpoint": "http://localhost:7000/connect/token",
"userinfo_endpoint": "http://localhost:7000/connect/userinfo",
"end_session_endpoint": "http://localhost:7000/connect/endsession",
"check_session_iframe": "http://localhost:7000/connect/checksession",
"revocation_endpoint": "http://localhost:7000/connect/revocation",
"introspection_endpoint": "http://localhost:7000/connect/introspect",
"device_authorization_endpoint": "http://localhost:7000/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": ["openid", "profile", "api1", "api2", "offline_access"],
"claims_supported": ["sub", "name", "family_name", "given_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "updated_at"],
"grant_types_supported": ["authorization_code", "client_credentials", "refresh_token", "implicit", "password", "urn:ietf:params:oauth:grant-type:device_code"],
"response_types_supported": ["code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token"],
"response_modes_supported": ["form_post", "query", "fragment"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"id_token_signing_alg_values_supported": ["RS256"],
"subject_types_supported": ["public"],
"code_challenge_methods_supported": ["plain", "S256"],
"request_parameter_supported": true
}
2)第二次请求IdentityServer授权服务,指向终点路由DiscoveryKeyEndPoint(/.well-known/openid-configuration/jwks),获取IdentityServer授权服务中jwks信息(kid和公钥)。kid唯一标识一个token
{
"keys": [{
"kty": "RSA",
"use": "sig",
"kid": "B22FBEC9ACABEF0F9DFFD4DFD370D445",
"e": "AQAB",
"n": "45H1Uw6EFjClHK-LQpUptFWX0PrpQEqy-YVNljj8cYmrPK3Zjqgk6XyW8tSCEYpClJJnuvSrae9yyrYdCghChDkNFVU1H8PT3Y_aSYDrULfqKO39HdqF7pZoxayZpyseAHE9tYQjtAw7E5IBpnXd_02Wz4K2mpt8Z2s5hPwos2_ze1Msvdl7iPmPfNdncl2tvERkr9pM5vRvbzxA9N1aTF58W03oq6bZoIA2w28FhTedcCxpPb3euT926ribOKCtsxCKCqFiIZYc0ovAiSV-kuLhaywd9e5KA18sOObg0McOBGmCJsQ6PTkkpn_yqvb3Eu9jhgxCO-_8K3Ml_dFIXQ",
"alg": "RS256"
}]
}
3)API服务根据kid本地缓存token资源,当同一个kid再次发起请求时,不再向授权服务identityserver请求。
4)通过授权服务拿到token的kid和公钥之后,由API资源服务验证token,我们看下AuthenticationMiddleware中间件做了什么
public IAuthenticationSchemeProvider Schemes { get; set; }
public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>((IAuthenticationFeature) new AuthenticationFeature()
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
IAuthenticationHandlerProvider handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (AuthenticationScheme authenticationScheme in await this.Schemes.GetRequestHandlerSchemesAsync())
{
IAuthenticationRequestHandler handlerAsync = await handlers.GetHandlerAsync(context, authenticationScheme.Name) as IAuthenticationRequestHandler;
bool flag = handlerAsync != null;
if (flag)
flag = await handlerAsync.HandleRequestAsync();
if (flag)
return;
}
AuthenticationScheme authenticateSchemeAsync = await this.Schemes.GetDefaultAuthenticateSchemeAsync();
if (authenticateSchemeAsync != null)
{
//实际核心认证处理的地方
AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticateSchemeAsync.Name);
if (authenticateResult?.Principal != null)
context.User = authenticateResult.Principal;
}
await this._next(context);
}
在认证过程中, Schemes.GetRequestHandlerSchemesAsync() 通过AuthenticationSchemeProvider获取Scheme列表,
handlers.GetHandlerAsync 通过IAuthenticationHandlerProvider 获取scheme对应的handler并存在handlerMap缓存字典里,
context.AuthenticateAsync 通过Scheme和AuthenticationHandlerProvider获取实际处理请求的AuthenticationHandler(如JwtBearerHandler),最后通过实际的AuthenticationHandler的AuthenticateAsync方法进行认证流程。
解析认证后会将token(JWT格式:header+payload+签名)中的Payload主体部分赋值到当前请求上下文context.User中。
然后我们便可以在API资源服务器中通过ClaimsPrincipal—User获取解析后的相关信息。
public ActionResult<IEnumerable<string>> Get()
{
var claims = User.Claims.Select(x => new { x.Type, x.Value }).ToList();
var claimSub = User.FindFirstValue("sub");
var claimId = User.FindFirstValue("id");
return new string[] { "Hello,WebAPI" };
}
2.资源鉴权
通过【Authorize】特性指定Controller或Action实现鉴权
[HttpGet]
[Authorize(Roles = "admin")]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "Hello,WebAPI" };
}
或者
通过AuthorizeFilter给Controller添加全局鉴权
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(x => x.Filters.Add(new AuthorizeFilter()));//全局添加权限认证
//将身份验证服务添加到管道中
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("Authority:Url"); //你要请求验证的identity服务端的地址
options.RequireHttpsMetadata = false;
options.ApiName = "api"; //你选择的验证方式。 对应的GetClients中定义的作用域
});
}
那么.Net框架内部又是如何识别上面两种方式来实现鉴权的呢?
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthorization();
}
通过 app.UseAuthorization(); 在Http请求管道中添加了授权中间件AuthorizationMiddleware。
public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
VerifyServicesRegistered(app);
return app.UseMiddleware<AuthorizationMiddleware>();
}
AuthorizationMiddleware 中间件中,我们发现了下面这行代码:
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
再看IAuthorizeData,定义了授权规则信息
public interface IAuthorizeData
{
string AuthenticationSchemes { get; set; }
string Policy { get; set; }
string Roles { get; set; }
}
不难看出,正是通过 endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() 获取了终点路由元素对应的授权规则信息。
那么又如何与AuthorizeAttribute特性和全局过滤器实现了AuthorizeFilter关联呢?继续往下看
先看AuthorizeAttribute特性
public class AuthorizeAttribute : Attribute, IAuthorizeData
{}
实际上 AuthorizeAttribute 实现了 IAuthorizeData 授权规则接口,这也就不难理解被标识了特性【Authorize】的Controller和Action是如何被发现的。
再看AuthorizeFilter过滤器,这里仅列出方法中部分代码,便于展示和理解
public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory,IFilterMetadata
{
public IEnumerable<IAuthorizeData> AuthorizeData { get; } //默认构造函数中默认创建了AuthorizeAttribute 对象
public AuthorizeFilter()
: this(authorizeData: new[] { new AuthorizeAttribute() })
{
} //赋值AuthorizeData
public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData)
{
if (authorizeData == null)
{
throw new ArgumentNullException(nameof(authorizeData));
} AuthorizeData = authorizeData;
}
}
实际上 AuthorizeFilter 在默认构造函数中实例化了 AuthorizeAttribute 对象,并且完成了 IAuthorizeData 集合的赋值。
这也就能理解为什么通过 IAuthorizeData 可以获取到过滤器AuthorizeFilter 对应的终点路由元素的授权规则信息。
从本质上来说以上两种方式都是关联实现了接口 IAuthorizeData ,因此直接通过 endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() 便可以获取到终点路由元素的授权规则信息。
我相信看了上述文章之后,最开始的3个疑惑大家应该内心都有自己的答案了。
1.client请求的时携带的Token具体是如何实现认证的?
首先API资源服务向IdentityServer认证服务请求获取token的公钥,然后API服务根据公钥解密token的加签部分,认证token是否有效。
2.Token中自包含的信息是如何传递到API资源服务器中的?
IdentityServer认证服务使用的是JWT类型的token,自包含了用户信息。当API服务认证token有效后,会在AuthenticationMiddleware中间件中对token进行解析获取token中payload部分的信息,并赋值到当前请求的context.User上下文当中。
3.为什么被标识了【Authorize】特性或者被全局设置过滤器AuthorizeFilter的资源就能实现鉴权,系统是如何实现的?
AuthorizationMiddleware授权中间件中通过 endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() 获取终点路由元素的授权规则,实现API资源鉴权。
深入了解身份认证和授权机制,看看API请求到底发生了什么?的更多相关文章
- Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(四)
在上一讲中,我们已经完成了一个完整的案例,在这个案例中,我们可以通过Angular单页面应用(SPA)进行登录,然后通过后端的Ocelot API网关整合IdentityServer4完成身份认证.在 ...
- Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(二)
上文已经介绍了Identity Service的实现过程.今天我们继续,实现一个简单的Weather API和一个基于Ocelot的API网关. 回顾 <Angular SPA基于Ocelot ...
- .NetCore WebApi——基于JWT的简单身份认证与授权(Swagger)
上接:.NetCore WebApi——Swagger简单配置 任何项目都有权限这一关键部分.比如我们有许多接口.有的接口允许任何人访问,另有一些接口需要认证身份之后才可以访问:以保证重要数据不会泄露 ...
- Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)
好吧,这个题目我也想了很久,不知道如何用最简单的几个字来概括这篇文章,原本打算取名<Angular单页面应用基于Ocelot API网关与IdentityServer4+ASP.NET Iden ...
- Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(三)
在前面两篇文章中,我介绍了基于IdentityServer4的一个Identity Service的实现,并且实现了一个Weather API和基于Ocelot的API网关,然后实现了通过Ocelot ...
- (转)Asp.Net MVC中身份认证和授权
MVC自带的ActionFilter 在Asp.Net WebForm的中要做到身份认证微软为我们提供了三种方式,其中最常用的就是我们的Form认证,需要配置相应的信息.例如下面的配置信息: < ...
- Core篇——初探Core的认证,授权机制
目录 1.Cookie-based认证的实现 2.Jwt Token 的认证与授权 3.Identity Authentication + EF 的认证 Cookie-based认证的实现 cooki ...
- ASP.NET Core 3.0 gRPC 身份认证和授权
一.开头聊骚 本文算是对于 ASP.NET Core 3.0 gRPC 研究性学习的最后一篇了,以后在实际使用中,可能会发一些经验之文.本文主要讲 ASP.NET Core 本身的认证授权和gRPC接 ...
- OAuth2.0认证和授权机制讲解
第一章.OAuth2.0 介绍 OAuth认证 OAuth认证是为了做到第三方应用在未获取到用户敏感信息(如:账号密码.用户PIN等)的情况下,能让用户授权予他来访问开放平台(主要访问平台中的资源服务 ...
- [认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)
0 目录 认证授权系列:http://www.cnblogs.com/linianhui/category/929878.html 1 什么是OIDC? 看一下官方的介绍(http://openid. ...
随机推荐
- 当微服务遇上 Serverless | 微服务容器化最短路径,微服务 on Serverless 最佳实践
简介: 阿里云Serverless应用引擎(SAE)初衷是让客户不改任何代码,不改变应用部署方式,就可以享受到微服务+K8s+Serverless的完整体验,开箱即用免运维. 前言 微服务作为一种更 ...
- AtCoder赛后反思
先贴上本人主页 ABC347 \(\color{blue}1624\color{red}-24\color{black}=\color{blue}1600\) 蓝名保卫战,极限 1600 C 题还是有 ...
- USB3.0与Type-C接口的关系
USB全称为Universal Serial Bus,翻译过来就是通用串行总线,是连接计算机与外部设备的一种串口总线标准.USB的发展经历了一下阶段: USB1.0:1.5Mbps(192KB/s)低 ...
- iOS中atomic修饰符的底层实现
在iOS中,atomic表示一个类的属性getter/setter具有原子性.那么iOS底层是如何保证这种原子性的呢? 我们有一个类A,它有一个属性X具有atomic: @property (atom ...
- nim 4. 模块
看了一下nim的模块系统,真的非常简洁. 1) 一个nim文件就是一个模块 2) 通过import 引入模块,引入的时候不需要带扩展名, 比如有个模块 mod1.nim, 引入的时候: import ...
- [popover, select] el-popover内有select的时候在选择后会自动关闭
Steps to reproduce 选择某个选项后会自动关闭 What is Expected? 选择后不自动关闭,等点击按钮后再去触发组件内的关闭方法. What is actually happ ...
- Python 潮流周刊#50:我最喜欢的 Python 3.13 新特性!
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- JDK源码阅读-------自学笔记(十)(java.lang.Integer包装类初探)
自动装箱和拆箱 JDK1.5后,Java引入了自动装箱(autoboxing)/拆箱(unboxing) 自动装箱 基本数据类型在需要时自动转化为对象 自动装箱 对象在需要时自动转化为基本数据类型 注 ...
- 使用XCA自制CA证书并签发https证书
序言 本文目的是使公司内网部署的Web可以使用https的方式访问 现有部署的系统有用域名访问,有用IP访问,但都是用http的方式 所以打算在公司内网部署统一的CA证书,并可以自己签发对应的域名和I ...
- AIRIOT答疑第4期|如何使用数据分析引擎?
灵活报表曲线,满足各类分析需求! AIRIOT物联网低代码平台的数据分析引擎满足各类型数据分类及分析需求,毫秒级数据反馈速度,快速响应客户分析条件变换查询需求.通过机器学习.融合各种计算模型.人工智能 ...