一款简单易用的可以为任意结构体生成认证 JWT 并解析 Token 为目标结构体的中间件。
关于认证模块简单的来说分为以下几个步骤:
- 认证用户信息
- 为用户生成对应的认证信息并生成 Token
- 解析 Token 拿到用户信息
- 使用用户信息
import "github.com/uptutu/auth"
user = User{
ID: "id code",
Name: "user name",
Avatar: "avatar url",
}
token, err = auth.NewSession(user).GenerateAccessToken()
// 设置 Cookie
// c *gin.Context
auth.SetCookie(c, token) // 使用认证中间件
// NewSession 函数中传入生成 Token 的结构体实例或指针皆可
// 解析到的用户信息会存在 ctx *gin.Context 中
r.Use(auth.Identify(User{}))
// 使用认证中间件
r.GET("/", func(c *gin.Context) {
data, err := auth.GetAuthenticationDataFrom(c, auth.ValueContextKey)
if err != nil {
c.AbortWithStatus(401)
return
}
// 使用自己定义的用户信息断言
user := data.(User)
c.JSON(200, gin.H{
"user": user,
})
})建议将从上下文和断言的代码放在一个函数中,可以复用
func GetAuthenticationData(ctx *gin.Context) (*User, error) {
d, err := auth.GetAuthenticationDataFrom(ctx, auth.ValueContextKey)
if err != nil {
return nil, errors.Wrap(err, "get authentication data failed")
}
a, ok := d.(User)
if !ok {
return nil, errors.Wrap(err, "get authentication data failed")
}
return &a, nil
}
func main() {
...
r.GET("/", func(c *gin.Context) {
user, err := GetAuthenticationData(c)
if err != nil {
c.AbortWithStatus(401)
return
}
c.JSON(200, gin.H{
"user": user,
})
}
...实现自己实现 auth.Session 的接口,用一个自己的结构体生成和解析 Token。
type Session interface {
GenerateAccessToken() (string, error)
ParseToken(ctx context.Context, token string) error
IsParsed() bool
SetExtIntoGinContext(ctx *gin.Context) error
GetExtFromGinContext(ctx *gin.Context) (interface{}, error)
GetToken(ctx *gin.Context) (string, error)
}
...
type customSession struct{}
func ( customSession )GenerateAccessToken() ( string, error) {
//TODO implement me
panic("your own custom token generator")
}
func ( customSession )ParseToken(ctx context.Context, token string) error {
//TODO implement me
panic("your own custom token parser")
}
func ( customSession )IsParsed() bool {
//TODO implement me
panic("implement me")
}
func ( customSession )SetExtIntoGinContext(ctx *gin.Context) error {
//TODO implement me
panic("implement me")
}
func ( customSession )GetExtFromGinContext(ctx *gin.Context) ( interface{}, error) {
//TODO implement me
panic("implement me")
}
func ( customSession )GetToken(ctx *gin.Context) ( string, error) {
//TODO implement me
panic("your own custom token generator")
}
中间件上的使用流程上也没有改变:
// 使用认证中间件
// NewSession 函数中传入生成 Token 的结构体实例或指针皆可
// 解析到的用户信息会存在 ctx *gin.Context 中
r.Use(auth.Identify(customSession{}))
// 使用认证中间件
r.GET("/", func(c *gin.Context) {
data, err := auth.GetAuthenticationDataFrom(c, asession{}.ValueContextKey())
if err != nil {
c.AbortWithStatus(401)
return
}
// 使用自己定义的用户信息断言
user := data.(asession)
c.JSON(200, gin.H{
"user": user,
})
})假设现在有一个接口是获取订单信息,检验该请求的订单 ID 是否为该用户的订单
func IsUserOrder(c *gin.Context, sess Session){
orderID := c.Request.Query("order_id")
authInfo, ok := sess.(User)
if !ok {
c.JSON(http.StatusInternalServerError, nil)
c.Abrode()
return
}
found := db.Select("1").From("orders").Where("user_id = ?", authInfo.ID).Where("id = ?", orderID).Exists()
if !found {
c.JSON(http.StatusBadRequest, )
c.Abrode()
return
}
}
...
r.Use(auth.Identify(auth.NewSession(User{}), IsUserOrder))
r.GET("/order/id", func(c *gin.Context) {
...
}MIT