JSON Web Token

简介: 本文介绍了 JWT(JSON Web Token)是什么,以及它的一些应用场景。

引言

本文由我的同事 @like 投稿,介绍了 JWT(JSON Web Token)是什么,以及它的一些应用场景。

背景

前段时间看 Kubernetes 认证相关的内容,发现如果 Pod 要和 API Server 交互,需要为该 Pod 创建 ServiceAccount 并绑定相应的角色。比如 Dashboard 的配置文件如下:

# ------------------- Dashboard Service Account ------------------- #

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kubernetes-dashboard-minimal
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kubernetes-dashboard-minimal
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system
# ------------------- Dashboard Deployment ------------------- #

kind: Deployment
apiVersion: apps/v1
metadata:
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  template:
    metadata:
      labels:
        k8s-app: kubernetes-dashboard
    spec:
      containers:
      - name: kubernetes-dashboard
        image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.1
      serviceAccountName: kubernetes-dashboard
---

这里创建了名称为 kubernetes-dashboard 的 ServiceAccount,并绑定到角色 kubernetes-dashboard-minimal,最后在 PodSpecserviceAccountName 字段引用了该 ServiceAccount。

究竟 ServiceAccount 中存放了哪些内容,Kubernetes 背后又做了哪些事情呢?

kubectl -n kube-system get serviceaccount kubernetes-dashboard -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  # ...
secrets:
- name: kubernetes-dashboard-token-sm862

与 ServiceAccount 关联的 Secret 中持有 API Server 的 CA 证书和签名的 JSON Web Token(JWT)。

kubectl -n kube-system get secret kubernetes-dashboard -o yaml
apiVersion: v1
data:
  ca.crt: (APISERVER'S CA BASE64 ENCODED)
  namespace: a3ViZS1zeXN0ZW0=
  token: (BEARER TOKEN BASE64 ENCODED)
kind: Secret
metadata:
  # ...
type: kubernetes.io/service-account-token

这个签名的 JWT 可以用作 bearer token 来认证该 ServiceAccount,即在 HTTP 请求中添加头部 Authorization: Bearer <JWT>。通常情况下,该 Secret 会被挂载到 Pod 下每个容器的 /var/run/secrets/kubernetes.io/serviceaccount 路径,用以 API 访问。

JWT

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

从JWT的定义看出其特点:

  • 信息格式为 json
  • 带签名,保证内容不被篡改

client-credentials-grant.png
目前 JWT 的主要用途是认证,用户成功登录后,认证服务器生成 token 并返回。接下来客户端所有的请求都会携带该 JWT,应用服务器验证 token 的合法性,识别出用户身份。

JWT数据结构

JWT 包含三部分内容,相互之间用 '.' 分隔

  • Header
  • Payload
  • Signature

所以 JWT 字符串看起来如下:

xxxxx.yyyyy.zzzzz

Header

头部由两部分组成: token 类型,为固定值 JWT;使用的签名算法,如 HMAC SHA256 或 RSA。
例子:

{
  "alg": "HS256",
  "typ": "JWT"
}

这个 JSON 对象经过 Base64Url 编码后形成 JWT 的第一部分。

Payload

JWT 的第二部分是 Payload,包含若干条声明(对用户的陈述),这些声明分为三种类型:

  • Registered claims - 预先定义的,包括以下7个字段, 注意:这些声明的名称都是三字符长度,所以说 JWT 是紧凑的

    1. iss (Issuer): 签发人
    2. sub (Subject): 主题
    3. aud (Audience): 受众
    4. exp (Expiration Time): 过期时间
    5. nbf (Not Before): 生效时间
    6. iat (Issued At): 签发时间
    7. jti (JWT ID): 编号
  • Public claims - 第三方扩展的,为避免冲突,使用前参考 IANA JSON Web Token Registry
  • Private claims - 由通信双方自定义的

同样,Payload 部分也要使用 Base64Url 编码。

Signature

Signature 部分是对 Header 和 Payload 的签名,防止数据篡改。
首先,指定一个密钥,该密钥只有认证服务器知道,然后根据 Header 中指定的签名算法,生成签名。

例如你想使用 HMAC SHA256 算法,那么生成签名的公式如下:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

签名能保证数据在传输过程中不被篡改,如果生成签名时使用的是私钥,能同时验证 JWT 生成方的身份。

注意:签名只保证信息不被篡改,Header 和 Payload 对任何人都是可读的。所以不要放一些敏感信息,除非是加密的。

最后把三部分字符串用逗号连接起来,就得到最终的 JWT。

实例

回到开头的例子,我们拿到了与 ServiceAccount 关联的JWT,利用官方提供的 Debugger 进行解码得到
Header:

{
  "alg": "RS256",
  "kid": ""
}

Payload:

{
  "iss": "kubernetes/serviceaccount",
  "kubernetes.io/serviceaccount/namespace": "kube-system",
  "kubernetes.io/serviceaccount/secret.name": "kubernetes-dashboard-token-sm862",
  "kubernetes.io/serviceaccount/service-account.name": "kubernetes-dashboard",
  "kubernetes.io/serviceaccount/service-account.uid": "91a26bae-6bf1-11e9-aea5-b06ebfc62c4b",
  "sub": "system:serviceaccount:kube-system:kubernetes-dashboard"
}

可以看出,Kubernetes 使用 RS256(RSA Signature With SHA-256)算法生成签名,默认使用的密钥是 API Server 的 TLS 私钥。这里采取 sub 字段的值作为当前的用户名, 进行后续鉴权。

注意: Kubernetes 的 Secret 中保存的数据是 base64 编码的,所以通过 kubectl 拿到的 token 需要先解码

另一个应用场景是 OpenID,在 OAuth 2.0 上扩展的认证协议,不在这里详细阐述。这里以 Google OAuth Playground 为例进行说明,整体过程与 OAuth 2.0 类似: 发送认证请求到 google -> 用户授权返回 code -> 拿 code 换取 access_token。不过这里的 scope 不再是需要授权的资源路径,而是 openid email/profile。认证 URL 可能如下:

https://accounts.google.com/o/oauth2/v2/auth?
 client_id=424911365001.apps.googleusercontent.com&
 response_type=code&
 scope=openid%20email&
 redirect_uri=https://oauth2-login-demo.example.com/code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps://oauth2-login-demo.example.com/myHome&
 login_hint=jsmith@example.com&
 openid.realm=example.com&
 nonce=0394852-3190485-2490358&
 hd=example.com

最后返回的结果除了 access_token,还包括 id_token(由 Google 生成的标明用户身份的 JSON Web Token)。下面让我们看下里面有什么内容
Header:

{
  "alg": "RS256",
  "kid": "57b1928f2f63329f2e92f4f278f94ee1038c923c",
  "typ": "JWT"
}

Payload

{
  "iss": "https://accounts.google.com",
  "azp": "407408718192.apps.googleusercontent.com",
  "aud": "407408718192.apps.googleusercontent.com",
  "sub": "xxx",
  "at_hash": "xxx",
  "name": "xxx",
  "picture": "xxx",
  "given_name": "xxx",
  "family_name": "xxx",
  "locale": "zh-CN",
  "iat": 1576927081,
  "exp": 1576930681
}

可以看到该 JWT 包含了用户名、头像、语言等 profile 信息。

如果我们拿到这样的 JWT,怎么验证其是否是 Google 生成的?

Google把公钥放在 https://www.googleapis.com/oauth2/v3/certs 这个路径上

{
    "alg": "RS256",
    "n": "1Zi0-4bNwZ7gGefz17U2NoKT4xBq-nzAa899teHxB2Q9KVCZYDhbQkpiIrBNg2u8s6TtoSljpq6MJpsKJVJgpT70gDCCgaUsGNYql9-kwWNKd80FlU1sjDEGouUIVEoYHzooPyn9r027KzMnTv5LGRYjxb5lvGnb4UCw5MF_EeSTNpGD7zb0b6juXwBxPi0oIUbQxAcGgH3oS40hXAjJ_U2T3Hln8lBlnVhLbrh-5qF-uoYDxjtAY9XyEJQH_rGiRfXWgBfSM02t9DCB46sQbEMM2iLe7mkGrZtCHR4zbAsAP0s2VGqSmwszNTWqqsdOccbfXp3i_ThkR3pDdTSIQQ",
    "use": "sig",
    "kid": "57b1928f2f63329f2e92f4f278f94ee1038c923c",
    "e": "AQAB",
    "kty": "RSA"
  }

可以看到,其 kid 与前面 JWT 的 header 中的 kid 匹配,下面我们用这个 key 来验证签名的合法性。

String e = <上面key的e字段>;
String n = <上面key的n字段>;
BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(n));
BigInteger exponent = new BigInteger(1, Base64.getUrlDecoder().decode(e));
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(spec);

String jwtStr = <你的id_token>;
Jwt jwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJwt(jwtStr);

执行上述代码后,会显示如下异常信息:

JWT expired at 2019-12-21T12:18:01Z. Current time: 2019-12-22T09:22:02Z, a difference of 75841420 milliseconds.

哈哈 过期啦😂

文章说明

更多有价值的文章均收录于贝贝猫的文章目录

stun

版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

创作声明: 本文基于下列所有参考内容进行创作,其中可能涉及复制、修改或者转换,图片均来自网络,如有侵权请联系我,我会第一时间进行删除。

参考内容

[1] JWT Introduction
[2] JSON Web Token入门教程
[3] Kubernetes Authenticating
[4] Google OpenID Connect

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
存储 JSON 安全
如何使用 JSON Web Tokens 进行身份验证?
总的来说,JWT 是一种强大而灵活的身份验证方式,通过正确使用和管理,可以为应用提供可靠的身份验证机制,同时提高系统的可扩展性和安全性。在实际应用中,需要根据具体的需求和场景,合理设计和实施 JWT 身份验证方案。
111 63
|
4月前
|
XML JSON 前端开发
【Web前端揭秘】XML与JSON:数据界的双雄对决,你的选择将如何改写Web世界的未来?
【8月更文挑战第26天】本文深入探讨了XML和JSON这两种广泛使用的数据交换格式在Web前端开发中的应用。XML采用自定义标签描述数据结构,适用于复杂层次数据的表示,而JSON则以键值对形式呈现数据,更为轻量且易解析。通过对两种格式的示例代码、结构特点及应用场景的分析,本文旨在帮助读者更好地理解它们的差异,并根据实际需求选择最合适的数据交换格式。
72 1
|
4月前
|
JSON 前端开发 JavaScript
|
2月前
|
存储 JSON 前端开发
JSON与现代Web开发:数据交互的最佳选择
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也便于机器解析和生成。它以文本格式存储数据,常用于Web应用中的数据传输,尤其是在客户端和服务器之间。
79 0
|
4月前
|
存储 JSON JavaScript
震撼!Cookie、Session、Token、JWT 终极对决:揭开 Web 认证的神秘面纱!
【8月更文挑战第13天】Web 开发中,Cookie、Session、Token 和 JWT 常混淆。Cookie 是服务器给客户端的小信息片,如登录状态,每次请求都会返回。Session 则是服务器存储的用户数据,通过 Session ID 追踪。Token 类似通行证,证明客户端身份且可加密。JWT 是结构化的 Token,含头部、载荷及签名,确保数据完整性和安全性。
75 4
|
4月前
|
JSON JavaScript 前端开发
Unexpected token u in JSON at position 0
这篇文章解释了JavaScript中"Unexpected token u in JSON at position 0"错误的常见原因,通常是由于尝试解析undefined变量导致的,并建议检查是否有变量在JSON.parse()执行时未赋值或值为undefined。
Unexpected token u in JSON at position 0
|
4月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
62 0
|
4月前
|
JSON Java API
解码Spring Boot与JSON的完美融合:提升你的Web开发效率,实战技巧大公开!
【8月更文挑战第29天】Spring Boot作为Java开发的轻量级框架,通过`jackson`库提供了强大的JSON处理功能,简化了Web服务和数据交互的实现。本文通过代码示例介绍如何在Spring Boot中进行JSON序列化和反序列化操作,并展示了处理复杂JSON数据及创建RESTful API的方法,帮助开发者提高效率和应用性能。
196 0
|
4月前
|
JSON Java API
【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?
【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?
105 0
|
4月前
|
前端开发 JavaScript
【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)
【Azure 环境】前端Web通过Azure AD获取Token时发生跨域问题(CORS Error)