升级到 Pulsar3.0 后深入了解 JWT 鉴权

背景
最近在测试将 Pulsar 2.11.2 升级到 3.0.1的过程中碰到一个鉴权问题,正好借着这个问题充分了解下 Pulsar 的鉴权机制是如何运转的。
Pulsar 支持 Namespace/Topic 级别的鉴权,在生产环境中往往会使用 topic 级别的鉴权,从而防止消息泄露或者其他因为权限管控不严格而导致的问题。

我们会在创建 topic 的时候为 topic 绑定一个应用,这样就只能由这个应用发送消息,其他的应用尝试发送消息的时候会遇到 401 鉴权的异常。
同理,对于订阅者也可以关联指定的应用,从而使得只有规定的应用可以消费消息。
鉴权流程
以上的两个功能本质上都是通过 Pulsar 的 admin-API 实现的。

这里关键的就是 role,在我们的场景下通常是一个应用的 AppId,只要是一个和项目唯一绑定的 ID 即可。
这只是授权的一步,整个鉴权流程图如下:

详细步骤
生成公私钥
bin/pulsar tokens create-key-pair --output-private-key my-private.key --output-public-key my-public.key
将公钥分发到 broker 的节点上,鉴权的时候 broker 会使用公钥进行验证。
而私钥通常是管理员单独保存起来用于在后续的步骤为客户端生成 token
使用私钥生成 token
之后我们便可以使用这个私钥生成 token 了:
bin/pulsar tokens create --private-key file:///path/to/my-private.key \
--subject 123456
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9
其中的
subject和本文长提到的role相等
使用 subject 授权
只是单纯生成了 token 其实并没有什么作用,还得将 subject(role) 与 topic 进行授权绑定。

也就是上图的这个步骤。
这里创建的
admin客户端也得使用一个superRole角色的token才有权限进行授权。
superRole使用在broker.conf中进行配置。
客户端使用 token 接入 broker
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://broker.example.com:6650/")
.authentication(AuthenticationFactory.token("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY"))
.build();
使用刚才私钥生成的 token 接入 broker 才能生产或者消费数据。
originalPrincipal cannot be a proxy role
这些流程正常都没啥问题,但直到我升级了 Pulsar3.0 后客户端直接就连不上了。
在 broker 中看到了 WARN 的警告日志:
cannot specify originalPrincipal when connecting without valid proxy role

之后在 3.0 的升级日志中看到相关的 Issue。
从这个 PR 相关的代码和变更的文档可以得知:


升级到 3.0 之后风险校验等级提高了,proxyRole 这个字段需要在 broker 中进行指定(之前的版本不需要强制填写)。
因为我们使用了 Proxy 组件,所有的请求都需要从 proxy 中转一次,这个 proxyRole 是为了告诉 broker:只有使用了 proxyRole 作为 token 的 Proxy 才能访问 broker,这样保证了 broker 的安全。
superUserRoles: broker-admin,admin,proxy-admin
proxyRoles: proxy-admin
以上是我的配置,我的 Proxy 配置的也是 proxy-admin 这个 token,所以理论上是没有问题的,但依然鉴权失败了,查看 broker 的日志后拿到以下日志:
Illegal combination of role [proxy-admin] and originalPrincipal [proxy-admin]: originalPrincipal cannot be a proxy role.
排查了许久依然没有太多头绪,所以我提了相关的 issue:
https://github.com/apache/pulsar/issues/21583
之后我咨询了 Pulsar 的 PMC @Technoboy 在他的提示下发现我在测试的时候使用的是 proxy-admin,正好和 proxyRoles 相等。

阅读源码和这个 PR 的 comment 之后得知:

也就是说客户端不能使用和 proxyRole 相同的角色进行连接,这个角色应当也只能给 Proxy 使用,这样的安全性才会高。
所以这个 Comment 还在讨论这是一个 breaking change? 还是一个增强补丁。
因为合并这个 PR 后对没有使用 proxyRole 的客户端将无法连接,同时也可能出现我这种 proxyRole 就是客户端使用的角色,这种情况也会鉴权失败。
所以我换了一个 superRole 角色就可以了,比如换成了 admin。
但其实即便是放到我们的生产系统,只要配置了
proxyRole也不会有问题,因为我们应用所使用的 role 都是不这里的superUserRole,全部都是使用AppId生成的。
token 不一致
但也有一个疑惑,我在换为存放在 configmap 中的 admin token 之前(测试环境使用的是 helm 安装集群,所以这些 token 都是存放在 configmap 中的),
为了验证是否只要非 proxyRole 的 superRole 都可以使用,我就自己使用了私钥重新生成了一个 admin 的 token。
bin/pulsar tokens create --private-key file:///pulsar/private/private.key --subject admin
这样生成的 token 也是可以使用的,但是我将 token 复制出来之后却发现 helm 生成的 token 与我用 pulsar 命令行生成的 token 并不相同。
为了搞清楚为什么 token 不同但鉴权依然可以通过的原因,之后我将 token decode之后知道了原因:


原来是 Header 不同从而导致最终的 token 不同,helm 生成的 token 中多了一个 typ 字段。
之后我检查了 helm 安装的流程,发现原来 helm 的脚本中使用的并不是 Java 的命令行工具:
${PULSARCTL_BIN} token create -a RS256 --private-key-file ${privatekeytmpfile} --subject ${role} 2&> ${tokentmpfile}
这个 PULSARCTL_BIN 是一个由 Go 写的命令行工具,我查看了其中的源码,才知道 Go 的 JWT 工具会自带一个 header。
https://github.com/streamnative/pulsarctl

而 Java 是没有这个逻辑的,但也只是加了 header,payload 的值都是相同的。
这样也就解释了为什么 token 不同但确依然能使用的原因。
升级到 Pulsar3.0 后深入了解 JWT 鉴权的更多相关文章
- xcode7.3 升级 xcode8.0 后权限设置问题(升级xcode 8.0 后构建版本不显示问题)
xcode7.3 升级 xcode8.0 后权限设置问题(升级xcode 8.0 后构建版本不显示问题) 前两天为了适配 iOS10 的系统 我将xcode 7.3 升级到了 xcode 8.0 但是 ...
- thinkjs升级到3.0后的图片上传
似乎当thinkjs升级到3.0后,才接手了一个项目.只是在实际运用过程中,还是发现了与2.2的些许差别——今天先分享关于图片上传的一些问题. 1.上传文件,我们选择了jQuery的插件:http:/ ...
- springboot升级到2.0后context-path配置不起作用
springboot升级到2.0后,context-path配置不起作用,改成了: server.servlet.context-path=/projname
- Android Studio 升级到3.0后出现编译错误\.gradle\caches\transforms-1\files-1.1\*****-release.aar
Android Studio 升级到3.0后出现各种编译问题,其中有一个问题是关于资源找不到的问题,百度了半天,也没有相关的文章 C:\Users.gradle\caches\transforms-1 ...
- HTTP基本认证和JWT鉴权
一.HTTP基本认证 Basic Authentication——当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: · 客户端发送http请求 · ...
- Spring Boot 鉴权之—— JWT 鉴权
第一:什么是JWT鉴权 1. JWT即JSON Web Tokens,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),他可以用来安全的传递信息,因为传递的信息是 ...
- JWT鉴权
一.HTTP基本认证 Basic Authentication--当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: 客户端发送http请求 服务器发 ...
- jwt鉴权学习 (php示例代码)
前段时间听朋友讲起 jwt鉴权,博主我是一脸懵逼,通过朋友坚持不懈的讲解,我终于听懂了,jwt就是登陆token校验嘛 然而事情并不是博主想象的那么简单,在一个艳阳高照,晴空万里的夜晚,博主手贱百度了 ...
- 关于Android Studio升级到2.0后和Gradle插件不兼容的问题
今天升级AS到2.0后,用AS在真机上调试,发现报了如下错误: This version of Android Studio is incompatible with the Gradle Plugi ...
- springboot 升级到2.0后 context-path 配置 不起作用,不生效 不管用 皆是因为版本改动导致的在这里记录一下
不知不觉,新的项目已经将springboot升级为2.0版本了.刚开始没有配置server.contextpath,默认的“/”,然后今天放到自己的服务器上,所以就要规范名称. 结果,失败了,无论我 ...
随机推荐
- 解决pandas 读取csv文件报错
使用encoding参数: pd.read_csv(path,sep=",",encoding='utf-16') 注意:该参数之后的编码格式,并不是固定的,需要用记事本打开csv ...
- [golang]gin框架接收websocket通信
前言 WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都 ...
- [ansible]常用内置模块
前言 ansible内置了很多模块,常用的并不多,可以通过ansible -l命令列出所有模块,使用 ansible-doc module-name 查看指定模块的帮助文档,例如:ansible-do ...
- Mybatis插件功能
1 插件的作用 在Mybatis执行SQL的生命周期中,会使用插件进行埋点,主要包括Executor.StatementHandler.ParameterHandler和ResultSetHandle ...
- Vue【原创】日历组件Calendar
最近项目中封装了一个日历组件,用于节假日管理,支持输入默认选中的日期,选择管理日期. 效果图: calendar组件: 1 <template> 2 <div class=" ...
- 揭秘ChatGPT,如何打造自己的自定义指令
一.ChatGPT-0720更新 又在深夜,正要打开ChatGPT官网测试下pdf对话功能,发现ChatGPT又有更新.本次更新总结有2点: 1.对于Plus用户,GPT-4的使用限额从25条/3h提 ...
- 避坑|在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次;|UI自动化测试|数据驱动
在读取excel.xlsx文件中的内容时发现明明只有3行,但跑起来却认为有13行,导致有10行None,UI自动化测试代码空跑了10次: 原因:excel.xlsx内容清除时用delete快捷键导致, ...
- 【反反爬】使用Jsoup爬取数据保存Excel
本文主要使用Jsoup爬取XXX房屋信息,抓取一些房屋信息,比如房屋楼盘.户型.价格.地址等信息,然后保存到Excel,便于对比和筛选,选出符合预期的好房. 注意,有些网站有防爬取机制,需要设置动 ...
- xv6 进程切换中的锁:MIT6.s081/6.828 lectrue12:Coordination 以及 Lab6 Thread 心得
引言 这节课和上一节xv6进程切换是一个完整的的进程切换专题,上一节主要讨论进程切换过程中的细节,而这一节主要讨论进程切换过程中锁的使用,所以本节的两大关键词就是"Coordination& ...
- mysql锁初识
MySQL锁的粒度分为:行级锁.表级锁.页级锁. 一.行级锁(INNODB引擎) 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁. 行级锁能大大减少数据库操作的冲突.其加锁粒 ...