IdentityServer4实战 - JWT Token Issuer 详解
原文:IdentityServer4实战 - JWT Token Issuer 详解
一.前言
本文为系列补坑之作,拖了许久决定先把坑填完。
下文演示所用代码采用的 IdentityServer4 版本为 2.3.0,由于时间推移可能以后的版本会有一些改动,请参考查看,文末附上Demo代码。
本文所诉Token如无特殊说明皆为 JWT Token。
众所周知 JWT Token 由三部分组成,第一部分 Header,包含 keyid、签名算法、Token类型;第二部分 Payload 包含 Token 的信息主体,如授权时间、过期时间、颁发者、身份唯一标识等等;第三部分是Token的签名。我们对一个 Token 进行解码,观察其中 Payload 部分,你将会发现一个 "iss" 字段,那么它代表什么呢,它又有什么作用呢,请看后文分解。
二. Issuer 的前世今生
iss 是 OpenId Connect(后文简称OIDC)协议中定义的一个字段,其全称为 “Issuer Identifier”,中文意思就是:颁发者身份标识,表示 Token 颁发者的唯一标识,一般是一个 http(s) url,如 https://www.baidu.com
。
在 Token 的验证过程中,会将它作为验证的一个阶段,如无法匹配将会造成验证失败,最后返回 HTTP 401。
三. Issuer 的验证流程分析
JWT的验证是去中心化的验证,实际这个验证过程是发生在API资源的,除了必要的从 IdentityServer4 获取元数据(获取后会缓存,不用重复获取)比如获取公钥用于验证签名,是不会再去交互的(详细介绍:https://www.cnblogs.com/stulzq/p/9226059.html)。那么我们就从 API 资源作为入口开始分析。
我们在 API 资源的配置认证的代码如下:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
// IdentityServer4 地址
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.Audience = "api1";
});
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
}
}
一个携带 Token 的请求从认证中间件到最终验证 Issuer 的逻辑如下图(懒得画流程图了,直接做个gif)。
(新窗口打开查看大图)
最终验证 Issuer 的代码:
public static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
//最终进入 验证Issuer逻辑
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
if (!validationParameters.ValidateIssuer)
{
LogHelper.LogInformation(LogMessages.IDX10235);
return issuer;
}
if (validationParameters.IssuerValidator != null)
return validationParameters.IssuerValidator(issuer, securityToken, validationParameters);
if (string.IsNullOrWhiteSpace(issuer))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211)
{ InvalidIssuer = issuer });
// Throw if all possible places to validate against are null or empty
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) && (validationParameters.ValidIssuers == null))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204)
{ InvalidIssuer = issuer });
if (string.Equals(validationParameters.ValidIssuer, issuer, StringComparison.Ordinal))
{
LogHelper.LogInformation(LogMessages.IDX10236, issuer);
return issuer;
}
if (null != validationParameters.ValidIssuers)
{
foreach (string str in validationParameters.ValidIssuers)
{
if (string.IsNullOrEmpty(str))
{
LogHelper.LogInformation(LogMessages.IDX10235);
continue;
}
if (string.Equals(str, issuer, StringComparison.Ordinal))
{
LogHelper.LogInformation(LogMessages.IDX10236, issuer);
return issuer;
}
}
}
throw LogHelper.LogExceptionMessage(
new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX10205, issuer, (validationParameters.ValidIssuer ?? "null"), Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)))
{ InvalidIssuer = issuer });
}
由源码分析可以获得几个结论:
1.验证 Token 必定会验证 Issuer,如果 Issuer 验证失败,那么表示则整个 Token 的验证结果就是失败。
- Issuer 默认从 IdentityServer4 的 Discovery Endpoint(/.well-known/openid-configuration)获取Issuer
3.Issuer 可以自定义,并且可以设置一个列表,如果手动设置了会覆盖默认值
4.Issuer 验证逻辑默认只验证是否相等,即 Token 携带的 Issuer 是否与 设置的 Issuer 值相等。
5.Issuer 验证逻辑可以自定义
6.Issuer 的验证可以关闭
以上设置如无特殊需求直接使用默认值即可,不需要额外设置。
关于以上结论的在代码(API资源)中的实现:
四.如何设置 Token 的 Issuer
第三节讲的是 Issuer 验证时有效 Issuer 的设置,本节讲的是 设置 Token 的 Issuer,Token携带的 Issuer 与API资源设置的有效 Issuer 进行验证匹配完成整个流程,这里提一下,避免搞混。
设置 Token 的 Issuer 需要在 IdentityServer4 设置。在 Startup 里中设置:
services.AddIdentityServer(option=>option.IssuerUri="https://www.baidu.com")
此值必须是一个 http(s) url。
验证是否生效:
1.访问 Discovery Endpoint(/.well-known/openid-configuration)
2.对Token解码,查看 iss 字段
如果在 IdentityServer4 设置此值,默认情况下所有API资源都会获取此值作为默认有效Issuer。
如果你自定义了 Issuer,在使用 Client 访问时会出现 Issuer 与 Authority 不匹配的错误,是因为Client在默认情况下作了限制,关闭即可:
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest(){Address = "http://localhost:5000" ,Policy = new DiscoveryPolicy(){ValidateIssuerName = false}});
五.默认值问题
如果没有手动设置 IdentityServer4 IssuerUri 值那么它默认会取你访问 IdentityServer4 时的 Host,下面举例说明。
首先修改 IdentityServer4 项目的监听地址,使其能够通过局域网IP访问
然后分别通过 localhost 和 局域网ip 访问 Discovery Endpoint,观察 Issuer 的值:
localhost:
局域网IP:
看出差异了吧,这一点需要注意,下一节将会讲一下这个引发的问题。
六.Issuer 默认值问题可能出现的场景及解决
这种情况一般出现在 IdentityServer4 经过了一层或多层代理,比如 Nginx反代、网关等,外网地址经过代理传递到了 IdentityServer4,如果直接通过外网请求的 Token Endpoint(/connect/token) 生成的 Token,那么这个 Token 携带的 iss 地址将会是外网地址(正常情况下,Host是会经过代理传过来的,如果你不配置传过来,那么就没有这个问题,那么你的后端服务获取的地址与预期肯定有差别,不推荐这种做法)。但是本地API资源(与IdentityServer4在同一台服务器或者同一个局域网)与IdentityServer4交互的地址(Authority)肯定会配成localhost 或者是局域网地址(如果你这里配置成外网地址,那么你可以不继续往下看了,内部交互还要走外部网络严重不推荐甚至是禁止此种做法)。
上图的架构即便是把 Gateway、IdentityServer、Basket服务(API资源)放在一台机器上也是一样的道理,都会出现这种情况,其原因就是如果 IdentityServer 不设置 Issuer,就会取你访问IdentityServer时的Host作为Issuer,外网进来的Host地址和你内部交互的不一样就造成了这个问题,解决办法就是在 IdentityServer 手动指定一个 Issuer 即可解决(第四节),取消掉它的默认取Host的机制,不管你怎么访问IdentityServer返回的Issuer都是一个地址。
七.结束
Demo:
https://github.com/stulzq/IdentityServer4.Samples/tree/master/Practice/03_Issuer
参考资料:
OIDC(OpenId Connect)身份认证授权(核心部分) by blackheart.
最后如果你觉得有用请点击右下角的“推荐”支持一下,十分感谢,写这篇博客花了不少功夫。
IdentityServer4实战 - JWT Token Issuer 详解的更多相关文章
- Asp.Net Core 中IdentityServer4 实战之角色授权详解
一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...
- JWT基础概念详解
JWT基础概念详解 JWT介绍 之前我们文章讲过分布式session如何存储,其中就讲到过Token.JWT.首先,我们来回顾一下使用Token进行身份认证. 客户端发送登录请求到服务器 服务器在用户 ...
- 微信公众开发URL和token填写详解
微信公众开发URL和token填写详解 方法/步骤 作为一名微信公众号开发者,别人进入你的微信公众号,肯定会看见某些网页,或者给你发某些信息,你需要实时自动回复,所以你需要一个24小时为用户服 ...
- Linq实战 之 DataSet操作详解
Linq实战 之 DataSet操作详解 一:linq to Ado.Net 1. linq为什么要扩展ado.net,原因在于给既有代码增加福利.FCL中在ado.net上扩展了一些方法. 简单一 ...
- Java工程师 基础+实战 完整路线图(详解版)
Java工程师 基础+实战 完整路线图(详解版) Java 基础 Java 是一门纯粹的面向对象的编程语言,所以除了基础语法之外,必须得弄懂它的 oop 特性:封装.继承.多态.此外还有泛型.反射 ...
- SpringCloud微服务项目实战 - API网关Gateway详解实现
前面讲过zuul的网关实现,那为什么今天又要讲Spring Cloud Gateway呢?原因很简单.就是Spring Cloud已经放弃Netflix Zuul了.现在Spring Cloud中引用 ...
- IdentityServer4实战 - JWT Issuer 详解
一.前言 本文为系列补坑之作,拖了许久决定先把坑填完. 下文演示所用代码采用的 IdentityServer4 版本为 2.3.0,由于时间推移可能以后的版本会有一些改动,请参考查看,文末附上Demo ...
- JSON Web Token(JWT)的详解
1.传统身份验证和JWT的身份验证 传统身份验证: HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用.这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户 ...
- JSON Web Token 使用详解
JWT 是什么? JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案.它是有三部分组成,示例如下,具体的讲解如下(jwt 是不会有空行的,下面只是为了显示,便使用了换行看着比较 ...
随机推荐
- 7.Node.js 创建第一个应用
转自:http://www.runoob.com/nodejs/nodejs-tutorial.html 如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器, ...
- ubuntu搭建交叉编译环境makeinfo: command not found
解决办法:sudo apt-get install texinfo
- vuex-store模块化配置
一.目录结构: src -> js -> modules 1. 在modules下新建文件夹,文件夹名称按模块功能命名 如: modules ---- home -> homeMod ...
- 模板 Fail树
fail树就是将Trie图的Fail指针反指,从而生成一棵树,这个树的性质是:子节点对应字符串为以当前串为后缀,而子节点为原串的前缀,前缀的后缀就是嵌套在原串中的子串. 模板:BZOJ3172 Des ...
- redirect_uri 参数错误
http://www.cnblogs.com/zitjubiz/p/5935712.html http://blog.csdn.net/u014033756/article/details/52038 ...
- 洛谷 P1068 分数线划定
P1068 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行.为了选拔最合适的人才,A 市对 所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试.面试分数线根 据 ...
- Google 免费公共 DNS 服务器
Google 免费公共 DNS 服务器 http://googleblog.blogspot.com/2009/12/introducing-google-public-dns.html DNS 8. ...
- Bitmap-把方形图片处理为圆形
这个是直接在网上转载的,自己验证可靠 转载自http://my.oschina.net/zhouz/blog/213164 直接贴上代码 import android.graphics.Bitmap; ...
- 1.10 Python基础知识 - 序列:列表
在Python中有很多的组合数据类型,其中包括列表,元组,字符串等数据类型,这些数据类型统称为序列类型,用他们可以处理复杂的数据. 列表,是一组有序元素组合的数据结构.列表是可变的数据类型. 列表采用 ...
- Java Web学习总结(17)——JSP属性范围
所谓的属性范围就是一个属性设置之后,可以经过多少个其他页面后仍然可以访问的保存范围. 一.JSP属性范围 JSP中提供了四种属性范围,四种属性范围分别指以下四种: 当前页:一个属性只能在一个页面中取得 ...