Fiber JWT
JWT返回一个JSON Web Token (JWT)认证中间件。对于有效的令牌,它会在Ctx.Locals中设置用户并调用下一个处理程序。对于无效的令牌,它会返回“401 - Unauthorized”错误。对于缺少的令牌,它会返回“400 - Bad Request”错误。
注意:需要Go 1.19及以上版本
安装
此中间件支持Fiber v1和v2,请相应安装。
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/jwt
go get -u github.com/golang-jwt/jwt/v5
签名
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error
配置
属性 | 类型 | 描述 | 默认值 |
---|---|---|---|
Filter | func(*fiber.Ctx) bool |
定义一个用于跳过中间件的函数 | nil |
SuccessHandler | func(*fiber.Ctx) error |
SuccessHandler定义一个在有效令牌上执行的函数。 | nil |
ErrorHandler | func(*fiber.Ctx, error) error |
ErrorHandler定义一个在无效令牌上执行的函数。 | 401 Invalid or expired JWT |
SigningKey | interface{} |
用于验证令牌的签名密钥。如果SigningKeys的长度为0,则用作备用。 | nil |
SigningKeys | map[string]interface{} |
用于验证带有kid字段的令牌的签名密钥映射。 | nil |
ContextKey | string |
用于将令牌中的用户信息存储到上下文中的上下文键。 | "user" |
Claims | jwt.Claim |
Claims是扩展的声明数据,定义了令牌内容。 | jwt.MapClaims{} |
TokenLookup | string |
TokenLookup是一个字符串,形如: ,用于解析令牌。 |
"header:Authorization" |
AuthScheme | string |
在Authorization header中使用的AuthScheme。默认值("Bearer" )仅与默认的TokenLookup 值一起使用。 |
"Bearer" |
KeyFunc | func() jwt.Keyfunc |
KeyFunc定义用户定义的函数,用于提供用于令牌验证的公钥。 | jwtKeyFunc |
JWKSetURLs | []string |
用于解析JWT的唯一JSON Web Key (JWK) Set URL的切片。 | nil |
HS256 示例
package main
import (
"time"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
// 登录路由
app.Post("/login", login)
// 未经身份认证的路由
app.Get("/", accessible)
// JWT 中间件
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
}))
// 受限制的路由
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// 抛出未授权错误
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// 创建声明
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// 创建令牌
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 生成编码的令牌并作为响应发送
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("可访问")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("欢迎 " + name)
}
HS256 测试
使用用户名和密码登录以获取令牌。
curl --data "user=john&pass=doe" http://localhost:3000/login
响应
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}
使用授权请求头中的令牌请求受限资源。
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
响应
欢迎 John Doe
RS256示例
package main
import (
"crypto/rand"
"crypto/rsa"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
jwtware "github.com/gofiber/contrib/jwt"
)
var (
// 显然,这只是一个测试示例。不要在生产中这样做。
// 在生产中,你应该事先生成密钥对。
// 绝不要将私钥添加到任何GitHub仓库中。
privateKey *rsa.PrivateKey
)
func main() {
app := fiber.New()
// 为了示范,在每次运行时生成一个新的私钥/公钥对。参见上面的说明。
rng := rand.Reader
var err error
privateKey, err = rsa.GenerateKey(rng, 2048)
if err != nil {
log.Fatalf("rsa.GenerateKey:%v", err)
}
// 登录路由
app.Post("/login", login)
// 未经身份验证的路由
app.Get("/", accessible)
// JWT中间件
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
JWTAlg: jwtware.RS256,
Key: privateKey.Public(),
},
}))
// 受限制的路由
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// 抛出未授权错误
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// 创建Claims
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// 创建token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// 生成编码的token并将其作为响应发送。
t, err := token.SignedString(privateKey)
if err != nil {
log.Printf("token.SignedString:%v", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("可访问")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("欢迎 " + name)
}
RS256测试
RS256实际上与上述HS256测试相同。
JWK Set测试
这些测试与上述基本JWT测试相同,但是需要提供JWK Set格式的有效公钥集合的JWKSetURLs。
自定义KeyFunc示例
KeyFunc定义了一个用户定义的函数,用于提供用于验证令牌的公钥。该函数需要负责验证签名算法并选择正确的密钥。如果令牌由外部方发行,则用户定义的KeyFunc可能会很有用。
当提供了用户定义的KeyFunc时,SigningKey、SigningKeys和SigningMethod将被忽略。这是三种提供令牌验证密钥的选项之一。优先顺序是用户定义的KeyFunc、SigningKeys和SigningKey。如果SigningKeys和SigningKey都未提供,则必须提供该函数。默认为验证签名算法并选择适当密钥的内部实现。
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
app.Use(jwtware.New(jwtware.Config{
KeyFunc: customKeyFunc(),
}))
app.Get("/ok", func(c *fiber.Ctx) error {
return c.SendString("OK")
})
}
func customKeyFunc() jwt.Keyfunc {
return func(t *jwt.Token) (interface{}, error) {
// 检查签名方法是否正确
if t.Method.Alg() != jwtware.HS256 {
return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
}
// TODO 自定义加载签名密钥的实现,例如从数据库中加载
signingKey := "secret"
return []byte(signingKey), nil
}
}