如何在 Golang 中创建和验证 JWT 和 PASETO 令牌
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
大家好!
在上一讲中,我们学习了基于令牌的身份验证,以及为什么 PASETO 在安全实践方面比 JWT 更好。
今天我们将学习如何在 Golang 中实现它们,以了解为什么 PASETO 比 JWT 更容易、更简单地实现。
以下是:
- YouTube 上的完整剧集播放列表链接
- 以及它的GitHub 仓库
声明令牌生成器接口
好了,我们开始吧!
首先,我将创建一个名为 的新包。然后在该包内token创建一个新文件。maker.go
我们的想法是声明一个通用token.Maker接口来管理令牌的创建和验证。之后,我们将编写一个实现该接口的结构JWTMaker体PasetoMaker。这样,我们就可以随时轻松地在不同类型的令牌生成器之间切换。
因此,该接口将包含 2 个方法:
type Maker interface {
CreateToken(username string, duration time.Duration) (string, error)
VerifyToken(token string) (*Payload, error)
}
该CreateToken()方法接受一个username字符串和一个有效期限duration作为输入。它返回一个已签名的令牌字符串或一个错误。简而言之,此方法将为指定的用户名和有效期限创建并签名一个新令牌。
第二个方法是VerifyToken()`getToken()`,它接受一个令牌字符串作为输入,并返回一个Payload对象或一个错误。我们Playload稍后会声明这个结构体。此VerifyToken()方法的作用是检查输入的令牌是否有效。如果有效,该方法将返回存储在令牌主体中的有效负载数据。
声明令牌有效载荷结构
好的,现在我们创建一个新payload.go文件,并Payload在其中定义结构体。这个结构体将包含令牌的有效载荷数据。
最重要的字段是Username,用于识别代币所有者。
然后IssuedAt添加一个字段,用于记录令牌何时创建。
在使用基于令牌的身份验证时,确保每个访问令牌的有效期较短至关重要。因此,我们需要一个ExpiredAt字段来存储令牌的过期时间。
type Payload struct {
ID uuid.UUID `json:"id"`
Username string `json:"username"`
IssuedAt time.Time `json:"issued_at"`
ExpiredAt time.Time `json:"expired_at"`
}
通常情况下,这三个字段就足够了。但是,如果我们想在特定令牌泄露时使其失效,则需要添加一个ID字段来唯一标识每个令牌。
这里我为该字段使用了 UUID 类型。该类型定义在google/uuid包中,因此我们需要运行go get命令将其下载并添加到项目中。
go get github.com/google/uuid
接下来,我将定义一个函数,NewPayload()该函数接受一个 `a`username和一个 `b`duration作为输入参数,并返回一个Payload对象或一个错误。此函数将创建一个包含特定用户名和有效期的新令牌有效负载。
func NewPayload(username string, duration time.Duration) (*Payload, error) {
tokenID, err := uuid.NewRandom()
if err != nil {
return nil, err
}
payload := &Payload{
ID: tokenID,
Username: username,
IssuedAt: time.Now(),
ExpiredAt: time.Now().Add(duration),
}
return payload, nil
}
首先,我们需要调用函数uuid.NewRandom()来生成唯一的令牌 ID。如果发生错误,我们只需返回空有效负载和错误本身即可。
否则,我们创建有效载荷,其中ID是生成的随机标记UUID,Username是输入username,IssuedAt是time.Now(),ExpiredAt是time.Now().Add(duration)。
然后我们只需返回这个有效负载对象和一个nil错误信息。就完成了!
实现 JWT 生成器
现在我们要实现一个功能JWTMaker。我们需要一个 Golang 的 JWT 包,所以让我们打开浏览器并搜索jwt golang。
可能有很多不同的软件包,但我认为这个是最受欢迎的:https://github.com/dgrijalva/jwt-go。所以我们复制它的 URL,然后go get在终端中运行命令来安装这个软件包:
go get github.com/dgrijalva/jwt-go
好了,软件包已安装完毕。现在我们回到 Visual Studio Code。
我将jwt_maker.go在token包内创建一个新文件。然后声明一个新的JWTMaker结构体类型。这个结构体是一个 JSON Web Token 生成器,它实现了token.Maker相应的接口。
在本教程中,我将使用对称密钥算法对令牌进行签名,因此该结构体将有一个字段来存储密钥。
type JWTMaker struct {
secretKey string
}
接下来,我们添加一个函数NewJWTMaker(),该函数接受一个secretKey字符串作为输入,并返回一个token.Maker接口或一个对象error作为输出。
通过返回接口,我们可以确保我们的对象JWTMaker必须实现该token.Maker接口。稍后我们将看到 Go 编译器是如何检查这一点的。
虽然我们将要使用的算法并不要求密钥长度,但为了提高安全性,最好还是确保密钥不要太短。因此,我将声明一个常量minSecretKeySize = 32字符。
const minSecretKeySize = 32
func NewJWTMaker(secretKey string) (Maker, error) {
if len(secretKey) < minSecretKeySize {
return nil, fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize)
}
return &JWTMaker{secretKey}, nil
}
然后,在这个函数内部,我们会检查密钥的长度是否小于minSecretKeySize或等于 32 个字符。如果小于或等于 32 个字符,则返回一个nil对象并报错,提示密钥必须至少包含 32 个字符。
否则,我们将返回一个JWTMaker包含输入的新对象secretKey和一个nil错误。
现在您可以看到这里有一条红线,因为JWTMaker我们创建的对象没有实现该token.Maker接口所需的方法,而该接口是此函数的返回类型。
所以为了解决这个问题,我们需要在这个结构体中添加CreateToken()and方法。VerifyToken()
让我们从界面复制它们,然后粘贴到这里。接下来,在每个方法前面token.Maker添加接收器。JWTMaker
func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, error) {}
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {}
好了,现在红线消失了!让我们来实现这个CreateToken()方法吧!
实现 JWT CreateToken 方法
首先,我们通过调用创建一个新的令牌有效负载NewPayload(),并传入输入username和有效值duration。
jwtToken如果 error 不为 nil,则返回一个空的 token 字符串和错误信息。否则,调用jwt.NewWithClaims()jwt-go 包中的函数创建一个新的 token 。
此函数需要 2 个输入参数:
- 首先是签名方法(或算法)。我将
HS256在本例中使用这种方法。 - 然后是声明,这实际上是我们创建的有效载荷。
func (maker *JWTMaker) CreateToken(username string, duration time.Duration) (string, error) {
payload, err := NewPayload(username, duration)
if err != nil {
return "", err
}
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
return jwtToken.SignedString([]byte(maker.secretKey))
}
最后,为了生成标记字符串,我们调用,并将转换为切片后jwtToken.SignedString()传入。secretKey[]byte
这里出现了一个错误,因为我们的Payload结构体没有实现该jwt.Claims接口。它缺少一个名为 `.` 的方法Valid()。
jwt-go 包需要这个方法来检查令牌负载是否有效。所以我们来payload.go添加这个方法。
该方法的签名非常简单。它不接受任何输入参数,仅在令牌无效时返回错误。您可以在 jwt-go 包的实现中轻松找到此方法。
var ErrExpiredToken = errors.New("token has expired")
func (payload *Payload) Valid() error {
if time.Now().After(payload.ExpiredAt) {
return ErrExpiredToken
}
return nil
}
最简单但也是最重要的一点是,我们必须检查令牌的过期时间。
如果time.Now()在之后payload.ExpiredAt,则表示令牌已过期。因此,我们只需返回一个新的错误信息:令牌已过期。
我们应该将此错误声明为公共常量:ErrExpiredToken,以便我们可以从外部检查错误类型。
如果令牌未过期,则直接返回nil。这样就完成了!Valid 函数编写完毕。
现在回到我们的jwt_maker.go文件,我们可以看到有效载荷对象上的红线消失了。
导入jwt-go软件包后,我们应该go mod tidy在终端中运行命令将其添加到go.mod文件中。
module github.com/techschool/simplebank
go 1.15
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.6.3
github.com/go-playground/validator/v10 v10.4.1
github.com/golang/mock v1.4.4
github.com/google/uuid v1.1.4
github.com/lib/pq v1.9.0
github.com/o1egl/paseto v1.0.0
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
)
我目前使用的 jwt-go 版本是 [版本号] 3.2.0。将来您可能会使用更新的版本,例如 [4.0版本号],届时函数和接口签名可能会有所不同。但是,基本思路应该类似。
好了,这个CreateToken()方法就完成了。接下来我们来看下一个VerifyToken()方法。
实现 JWT VerifyToken 方法
这会稍微复杂一些。首先,我们需要调用解析令牌的函数jwt.ParseWithClaims,并传入输入token字符串、一个空Payload对象和一个键函数。
什么是密钥函数?简单来说,它是一个接收已解析但未经验证的令牌的函数。您应该验证其头部信息,以确保其签名算法与您通常用于签署令牌的算法相匹配。
如果匹配成功,则返回密钥,以便 jwt-go 可以使用它来验证令牌。正如我在上一讲中解释的那样,这一步对于防止简单的攻击机制至关重要。
好的,我要复制这个函数签名,然后粘贴到这里。我们把这个输入参数的名称设为 `<name>` token,类型设为 `<type>` jwt.Token。然后我们只需将 `keyFunc` 传递给ParseWithClaims()调用即可。
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {
keyFunc := func(token *jwt.Token) (interface{}, error) {
_, ok := token.Method.(*jwt.SigningMethodHMAC)
if !ok {
return nil, ErrInvalidToken
}
return []byte(maker.secretKey), nil
}
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
...
}
在密钥函数中,我们可以通过token.Method字段获取其签名算法。请注意,它的类型是 `A` SigningMethod,也就是一个 `A` interface。因此,我们需要尝试将其转换为特定的实现。
在我们的例子中,我们将其转换为,SigningMethodHMAC因为我们使用的是HS256,它是结构体的实例SigningMethodHMAC。
这种转换可能成功,也可能失败。如果转换失败,则意味着该代币的算法与我们的签名算法不匹配,因此它显然是一个无效代币。
我们需要返回一个nil带有 . 的键ErrInvalidToken。我将在payload.go文件中声明这个新的错误,位置与 . 相同ErrExpiredToken。它们是我们的函数将返回的不同类型的错误VerifyToken()。
var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token has expired")
)
好的,回到正题JWTMaker。如果转换成功,则表示算法匹配。我们可以直接返回用于对令牌进行签名的密钥(在将其转换为[]byte切片之后),并返回一个空值错误。
好了,现在密钥函数已经准备就绪。我们继续ParseWithClaims调用这个函数。如果它返回非空错误,则可能存在两种情况:令牌无效或已过期。
但现在,当我们想要区分这两种情况时,事情就变得更加复杂了。如果我们跟踪 jwt-go 包的实现,就会发现它token.Claims.Valid()在底层会自动调用函数。
在我们的函数实现中,我们返回了ErrExpiredToken错误。然而,jwt-go 却将这个原始错误隐藏在了它自己的ValidationError对象中。
因此,为了确定真正的错误类型,我们必须将函数返回的错误转换ParseWithClaims()为jwt.ValidationError
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {
...
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
if err != nil {
verr, ok := err.(*jwt.ValidationError)
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
return nil, ErrExpiredToken
}
return nil, ErrInvalidToken
}
...
}
这里我将转换后的错误值赋给一个verr变量。如果转换成功,我们使用该errors.Is()函数来检查它是否verr.Inner真的是目标值ErrExpiredToken。
如果是,我们就返回nil有效载荷和ErrExpiredToken。否则,我们就返回 nil 和ErrInvalidToken。
如果一切顺利,令牌已成功解析和验证,我们将尝试将其转换jwtToken.Claims为 Payload 对象以获取其有效负载数据。
func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) {
keyFunc := func(token *jwt.Token) (interface{}, error) {
_, ok := token.Method.(*jwt.SigningMethodHMAC)
if !ok {
return nil, ErrInvalidToken
}
return []byte(maker.secretKey), nil
}
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
if err != nil {
verr, ok := err.(*jwt.ValidationError)
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
return nil, ErrExpiredToken
}
return nil, ErrInvalidToken
}
payload, ok := jwtToken.Claims.(*Payload)
if !ok {
return nil, ErrInvalidToken
}
return payload, nil
}
如果失败,则直接返回nil。ErrInvalidToken否则,返回有效负载对象和一个nil错误。
好了!项目JWTMaker完成了。现在让我们来编写一些单元测试吧!
测试 JWT 生成器
我将在包jwt_maker_test.go内创建一个新文件token。然后,我们来添加一个TestJWTMaker()以testing.T对象作为输入的新函数。
首先,我们需要调用该NewJWTMaker()函数创建一个新的生成器,并传入一个长度为 32 个字符的随机密钥。这里不需要返回任何错误。
接下来,我们使用username该util.RandomOwner()函数生成一个,假设有效的令牌duration将是1 minute。
我们还声明了两个变量,以便稍后比较结果:
- 时间
issuedAt应该是time.Now() - 我们将该时间加到
duration此issuedAt时间上,得到expiredAt令牌的时间。
func TestJWTMaker(t *testing.T) {
maker, err := NewJWTMaker(util.RandomString(32))
require.NoError(t, err)
username := util.RandomOwner()
duration := time.Minute
issuedAt := time.Now()
expiredAt := issuedAt.Add(duration)
token, err := maker.CreateToken(username, duration)
require.NoError(t, err)
require.NotEmpty(t, token)
payload, err := maker.VerifyToken(token)
require.NoError(t, err)
require.NotEmpty(t, token)
require.NotZero(t, payload.ID)
require.Equal(t, username, payload.Username)
require.WithinDuration(t, issuedAt, payload.IssuedAt, time.Second)
require.WithinDuration(t, expiredAt, payload.ExpiredAt, time.Second)
}
好的,现在我们通过调用函数生成令牌maker.CreatToken,并传入参数username。duration要求不出现任何错误,并且要求输出令牌不能为空。
接下来,我们调用函数maker.VerifyToken来验证令牌是否有效,并获取其有效负载数据。我们要求没有错误,并且有效负载对象不能为空。
接下来我们需要检查有效负载对象的所有字段。
- 首先,该值
payload.ID不能为零。 - 那么它
payload.Username应该等于输入username。 - 我们会将实际结果与之前预估的节省时间
require.WithinDuration进行比较。两者之间的差异不应超过 1 秒。payload.IssuedAtissuedAt - 同样地,我们用同样的方法将
payload.ExpiredAt场强与预期时间进行比较。expiredAt
完成了!让我们运行这个单元测试!
通过了。太好了!这就是我们测试正常情况的方法。
现在我们再添加一个测试,来检查 JWT 令牌过期的情况。
与之前类似,我们首先需要创建一个新的令牌JWTMaker。然后我们将通过调用来创建一个过期的令牌maker.CreateToken(),传入一个随机数username和一个值negative duration。
func TestExpiredJWTToken(t *testing.T) {
maker, err := NewJWTMaker(util.RandomString(32))
require.NoError(t, err)
token, err := maker.CreateToken(util.RandomOwner(), -time.Minute)
require.NoError(t, err)
require.NotEmpty(t, token)
payload, err := maker.VerifyToken(token)
require.Error(t, err)
require.EqualError(t, err, ErrExpiredToken.Error())
require.Nil(t, payload)
}
我们要求不得返回任何错误,且令牌不能为空。现在我们将验证此输出令牌。
这次,我们预期会返回一个错误。更具体地说,它应该是ErrExpiredToken……。最后,输出有效负载应该是nil……。
好的,我们来运行测试!
通过了。太好了!
我们要编写的最后一个测试是检查无效令牌的情况,其中None使用了算法标头。这是一种众所周知的攻击技术,我在上一节课中已经讲过。
首先,我将创建一个新的令牌,payload使用随机数username和duration1 分钟的有效期。要求无错误。然后,让我们通过调用并传入新令牌来创建一个新jwt.NewWithClaims()的jwt.SigningMethodNone令牌payload。
现在我们需要使用该SignedString()方法对令牌进行签名。但是我们不能随意使用任何随机密钥,因为 jwt-go 库完全禁止使用该None算法对令牌进行签名。
只有当传入这个特殊常量jwt.UnsafeAllowNoneSignatureType作为密钥时,才能将其用于测试。
如果你仔细阅读这个值的实现说明,你会发现通常情况下,除非输入的键是这个特殊常量,否则 `None` 符号是不允许使用的。这基本上意味着你清楚自己在做什么。请确保仅在测试环境中使用它,不要用于生产环境。
func TestInvalidJWTTokenAlgNone(t *testing.T) {
payload, err := NewPayload(util.RandomOwner(), time.Minute)
require.NoError(t, err)
jwtToken := jwt.NewWithClaims(jwt.SigningMethodNone, payload)
token, err := jwtToken.SignedString(jwt.UnsafeAllowNoneSignatureType)
require.NoError(t, err)
maker, err := NewJWTMaker(util.RandomString(32))
require.NoError(t, err)
payload, err = maker.VerifyToken(token)
require.Error(t, err)
require.EqualError(t, err, ErrInvalidToken.Error())
require.Nil(t, payload)
}
好了,我们回到代码。我们需要JWTMaker像其他测试一样创建一个新的令牌。现在我们调用函数maker.VerifyToken()来验证上面签名的令牌。
这次,该函数也应该返回一个错误,并且错误值应该等于ErrInvalidToken。输出有效负载也应该是nil。
好了,现在让我们运行测试!
通过了!太棒了!
现在您知道如何在 Go 语言中实现和测试 JWT 了。
虽然我认为 jwt-go 包在防止安全错误方面做得相当不错,但它仍然比必要的要复杂和难以使用,尤其是在令牌验证部分。
实现 PASETO Maker
现在我将向您展示如何使用 PASETO 实现相同的令牌生成器接口。这比 JWT 更简单、更简洁。
好的,我们打开浏览器搜索paseto golang。打开它的 GitHub 页面并复制 URL:https://github.com/o1egl/paseto。然后go get使用此 URL 运行命令下载软件包:
go get github.com/o1egl/paseto
现在回到我们的项目,我将在文件夹paseto_maker.go内创建一个新文件token。
类似于我们对 JWT 所做的,让我们声明一个类型PasetoMaker结构,它将实现相同的token.Maker接口,但使用 PASETO 而不是 JWT。
我们目前将使用最新版本的 PASETO,即版本 2。因此,PasetoMaker结构体将有一个paseto类型为 的字段paseto.V2。
type PasetoMaker struct {
paseto *paseto.V2
symmetricKey []byte
}
由于我只想在本地将此令牌用于我们的银行 API,我们将使用对称加密来加密令牌有效负载。因此,我们需要一个字段来存储它symmetricKey。
好的,现在我们来添加一个函数NewPasetoMaker(),该函数接受一个symmetricKey字符串作为输入,并返回一个token.Maker接口或对象error。该函数将创建一个新的PasetoMaker实例。
Paseto 版本 2 使用Chacha20 Poly1305算法对有效载荷进行加密。因此,我们需要检查对称密钥的长度,以确保其符合算法要求。
import (
"github.com/aead/chacha20poly1305"
"github.com/o1egl/paseto"
)
func NewPasetoMaker(symmetricKey string) (Maker, error) {
if len(symmetricKey) != chacha20poly1305.KeySize {
return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize)
}
maker := &PasetoMaker{
paseto: paseto.NewV2(),
symmetricKey: []byte(symmetricKey),
}
return maker, nil
}
如果键长不正确,则返回一个 nil 对象,并报错提示“键长无效”。键长必须正好是指定的字符数。
否则,我们只需创建一个新的 PasetoMaker 对象,其中包含转换为切片的paseto.NewV2()输入。symmetricKey[]byte
然后我们返回该maker对象和一个nil错误。
同样,这里我们在 maker 对象下方看到一条红线,因为它尚未实现该token.Maker接口。所以,让我们对它执行与之前相同的操作JWTMaker。
我将复制令牌生成器接口的这两个必需方法,并PasetoMaker在它们前面添加接收器。
func (maker *PasetoMaker) CreateToken(username string, duration time.Duration) (string, error) {}
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {}
好了,现在红线消失了。让我们来实现这个CreateToken()方法。
实现 PASETO CreateToken 方法
与之前类似,我们首先需要使用payload输入username和创建一个新的对象duration。如果错误不是nil,我们将返回一个空字符串和错误信息给调用者。
否则,我们返回maker.paseto.Encrypt(),并将参数maker.symmetricKey和payload对象传递给它。最后一个参数是可选的页脚,我们不需要它,所以我把它放在nil这里。
func (maker *PasetoMaker) CreateToken(username string, duration time.Duration) (string, error) {
payload, err := NewPayload(username, duration)
if err != nil {
return "", err
}
return maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
}
就是这样!很简短,对吧?
如果我们跟踪该函数的实现过程Encrypt(),就会发现它使用的是Chacha Poly密码算法。
函数内部newCipher()还会检查输入,key size确保它等于某个值32 bytes。
实现 PASETO VerifyToken 方法
好了,现在我们回到代码,实现这个VerifyToken()方法。非常简单!
我们只需要声明一个空payload对象来存储解密后的数据。然后调用该对象,maker.paseto.Decrypt()传入输入token、值symmetricKey、值payload和nil页脚。
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
payload := &Payload{}
err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil)
if err != nil {
return nil, ErrInvalidToken
}
err = payload.Valid()
if err != nil {
return nil, err
}
return payload, nil
}
如果error不是nil,则返回nil有效负载ErrInvalidToken。否则,我们将通过调用来检查令牌是否有效payload.Valid()。
如果出现错误,我们只返回nil有效负载和对象error本身。否则,我们返回对象payload和一个nil错误信息。
就是这样!非常简洁,比JWT简单多了,对吧?
测试 PASETO Maker
好了,现在我们来编写一些单元测试!
我将在包paseto_maker_test.go内创建一个新文件token。实际上,这个测试几乎和我们之前为 JWT 编写的测试完全相同,所以我直接把它复制到这里。
将其名称改为。然后在这里,我们TestPasetoMaker用代替。NewJWTMaker()NewPasetoMaker()
func TestPasetoMaker(t *testing.T) {
maker, err := NewPasetoMaker(util.RandomString(32))
require.NoError(t, err)
username := util.RandomOwner()
duration := time.Minute
issuedAt := time.Now()
expiredAt := issuedAt.Add(duration)
token, err := maker.CreateToken(username, duration)
require.NoError(t, err)
require.NotEmpty(t, token)
payload, err := maker.VerifyToken(token)
require.NoError(t, err)
require.NotEmpty(t, token)
require.NotZero(t, payload.ID)
require.Equal(t, username, payload.Username)
require.WithinDuration(t, issuedAt, payload.IssuedAt, time.Second)
require.WithinDuration(t, expiredAt, payload.ExpiredAt, time.Second)
}
我们无需更改其他任何内容,因为PasetoMaker它实现了与相同的token.Maker接口JWTMaker。
让我们运行测试!
通过了!
现在让我们复制过期令牌情况的测试!将其名称更改为TestExpiredPasetoToken,并将此调用更新为NewPasetoMaker()。
func TestExpiredPasetoToken(t *testing.T) {
maker, err := NewPasetoMaker(util.RandomString(32))
require.NoError(t, err)
token, err := maker.CreateToken(util.RandomOwner(), -time.Minute)
require.NoError(t, err)
require.NotEmpty(t, token)
payload, err := maker.VerifyToken(token)
require.Error(t, err)
require.EqualError(t, err, ErrExpiredToken.Error())
require.Nil(t, payload)
}
然后运行测试!
也通过了。太好了!
我们不需要最后一个测试,因为NonePASETO 中根本不存在这种算法。如果你愿意,可以编写另一个测试来检查无效标记的情况。我把它留给你作为练习。
本次讲座到此结束。我们学习了如何使用 Go 语言实现 JWT 和 PASETO 来创建和验证访问令牌。
在下一篇文章中,我将向您展示如何在登录 API 中使用它们,用户提供用户名和密码,如果提供的凭据正确,服务器将返回访问令牌。
非常感谢您的阅读,我们下节课再见!
如果您喜欢这篇文章,请订阅我们的YouTube 频道,并在Twitter或Facebook上关注我们,以便将来获取更多教程。
如果你想加入我在Voodoo的优秀团队,请点击此处查看我们的招聘信息。可远程办公,也可在巴黎/阿姆斯特丹/伦敦/柏林/巴塞罗那现场办公,公司提供签证担保。
文章来源:https://dev.to/techschoolguru/how-to-create-and-verify-jwt-paseto-token-in-golang-1l5j








