jwt - go
身份验证使应用程序知道向应用程序发送请求的人是谁。JSON Web 令牌(JWT)是一种允许身份验证的方法,而无需在系统本身实际存储任何有关用户的任何信息(与基于会话的身份验证相反 )。
在本文中,我们将演示基于 JWT 的身份验证的工作原理,以及如何在 Go 中构建示例应用程序以实现该示例。
如果你已经知道 JWT 的工作原理,并且只想看一下实现,则可以 跳过,或者在 Github 上查看源代码 。
JWT 格式
假设我们有一个名为的用户 user1,他们尝试登录到应用程序或网站。一旦成功,他们将收到一个看起来像这样的令牌:
1 |
|
这是一个 JWT,由三部分组成(以分隔.):
- 第一部分是标题 header(
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
)。标头指定信息,例如用于生成签名的算法(第三部分)。这部分是标准的,并且对于使用相同算法的任何 JWT 都是相同的。 - 第二部分是有效负载 payload (
eyJ1c2VybmFtZSI6InVzZXIxIiwiZXhwIjoxNTQ3OTc0MDgyfQ
),其中包含特定于应用程序的信息(在我们的示例中,这是用户名),以及有关令牌的到期和有效性的信息。 - 第三部分是签名(
2Ye5_w1z3zpD4dSGdRp3s98ZipCNQqmsHRB9vioOx54
)。它是通过组合和散列前两个部分以及一个秘密密钥来生成的。
现在有趣的是,标题 header 和有效负载 payload 未加密。它们只是 base64 编码的。这意味着任何人都可以通过解码来查看其内容。
例如,我们可以使用此 在线工具 对标题或有效负载进行解码。
eyJ1c2VybmFtZSI6InVzZXIxIiwiZXhwIjoxNTQ3OTc0MDgyfQ
将显示为以下内容:
1 |
|
JWT 签名如何工作
因此,如果任何人都可以读写 JWT 的标头和签名,那么实际上如何保证 JWT 是安全的?答案在于如何生成最后一部分(签名)。
假设你的应用程序想要向成功登录的用户 user1 签发 JWT。
使标头和有效负载非常简单:标头或多或少是固定的,有效负载 JSON 对象是通过设置用户 ID 和有效时间(以 Unix 毫秒为单位)来形成的。
发行令牌的应用程序还拥有一个密钥,该密钥是一个私有值,并且仅对应用程序本身是已知的。然后将标头和有效负载的 base64 表示形式与密钥组合,然后通过哈希算法计算签名值(在本例中为 HS256,如标头中所述)
如何实现算法的细节超出了本文的讨论范围,但是要注意的重要一点是,这是一种 hash 方法,这意味着我们无法破解算法并获得进行签名的密钥,因此我们秘密密钥仍然是私有的。
验证 JWT
为了验证传入的 JWT,将使用传入的 JWT 的标头和有效负载以及密钥再次生成签名。如果签名与 JWT 上的签名匹配,则认为 JWT 有效。
现在,让我们假设你是一个试图发行假令牌的黑客。你可以轻松地生成标头和有效负载,但是在不知道密钥的情况下,无法生成有效的签名。如果你尝试篡改有效 JWT 的有效负载 payload,则签名将不再匹配。
这样,JWT 可以以一种安全的方式授权用户,而无需在应用程序服务器上实际存储任何信息(除了密钥)。
GO 的实现
现在,我们已经了解了基于 JWT 的身份验证的工作原理,让我们使用 Go 来实现它。
创建 HTTP 服务器
首先让我们初始化需要使用的 HTTP 服务器路由:
1 |
|
现在,我们可以定义 Signin
和 Welcome
路由。
处理用户登录
/signin
路由将获取用户凭据并登录。为简化起见,我们在代码中将用户信息存储在 map:
1 |
|
因此,目前,我们的应用程序中只有两个有效用户: user1 和 user2。接下来,我们可以编写 SigninHTTP 处理程序。对于此示例,我们使用 dgrijalva/jwt-go 库来帮助我们创建和验证 JWT 令牌。
1 |
|
如果用户使用正确的凭据登录,则此处理程序将使用 JWT 值在客户端设置 cookie。一旦在客户端上设置了 cookie,此后它将与每个请求一起发送。现在,我们可以编写 Welcome 方法来处理用户特定的信息。
处理认证后的路由
现在,所有已登录的客户端都使用 cookie 存储用户信息,我们可以将其用于:
- 验证后续用户请求
- 获取有关发出请求的用户的信息
让我们编写 Welcome
处理方法来做到这一点:
1 |
|
双令牌续签
双令牌:刷新令牌 + 访问令牌
1、刷新令牌的低使用频率和安全性
在双令牌系统中,访问令牌和刷新令牌的角色和使用频率有明显差异:
- 访问令牌:用于访问系统资源,因此会频繁使用,每次API调用都可能需要验证访问令牌。这意味着访问令牌更频繁地在客户端和服务器之间传输,从而增加了被截获的风险。
- 刷新令牌:主要用于在访问令牌过期后获取新的访问令牌。因此,其使用频率远低于访问令牌,通常只在访问令牌失效时才使用。
安全优势
刷新令牌的这种使用模式带来了几个安全优势:
- 降低暴露风险:由于刷新令牌不需要频繁发送,其被截获的机会相对较小。这减少了被盗用的风险。
- 控制和监控:刷新令牌的使用可以更容易地被监控和控制,因为其使用情况较少且具有特定目的(仅用于令牌续签)。这使得任何异常使用模式更容易被检测到。
- 更长的生命周期管理:虽然刷新令牌具有更长的有效期,但由于其使用频率低,可以在系统检测到安全问题时及时撤销,而不像访问令牌那样经常处于活跃状态中。
为了最大程度地减少对 JWT 的滥用,通常将到期时间保持在几分钟左右。通常,客户端应用程序将在后台刷新令牌。
jwt + redis续签
JWT 实现登录认证 + Token 自动续期方案,这才是正确的使用姿势!
安全性:使用JWT作为session_id,其自带的签名验证确保了令牌的安全性,防止篡改。
状态管理:通过在Redis中存储prefix + session_id对应的用户信息,您可以轻松管理用户状态,如权限、偏好设置等。
可扩展性:Redis具有出色的读写性能,适用于大规模应用,且易于水平扩展。
灵活性:每次API访问时刷新Redis中的过期时间可以灵活地管理会话有效期,允许用户在活跃时保持登录状态。