在上一篇登录功能的实现中,我们使用了jwt
作为鉴权组件,其中登录后会颁发token
。前端在访问后续请求时,可以带上这个token
。对于一些需要权限校验的请求,我们就需要验证这个token
,从token
中获取到用户id
(有了用户id
,用户详情可以查DB
)。
此时我们很容易就想到了中间件,定义好鉴权中间件,对于需要鉴权的路由,加上这个中间件即可。
直接上代码吧,注释非常清晰
middlewares/auth.go
package middlewares
import (
"bluebell/constdef"
"bluebell/controller"
"bluebell/pkg/jwt"
"strings"
"github.com/gin-gonic/gin"
)
// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
// 这里假设Token放在Header的Authorization中,并使用Bearer开头
// Authorization: Bearer xxxxxxx.xxx.xxx / X-TOKEN: xxx.xxx.xx
// 这里的具体实现方式要依据你的实际业务情况决定,和前端对其即可
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
controller.ResponseError(c, controller.CodeNeedLogin)
c.Abort()
return
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
controller.ResponseError(c, controller.CodeInvalidToken)
c.Abort()
return
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
mc, err := jwt.ParseToken(parts[1])
if err != nil {
controller.ResponseError(c, controller.CodeInvalidToken)
c.Abort()
return
}
// 将当前请求的userId信息保存到请求的上下文c上
c.Set(constdef.CtxUserIDKey, mc.UserID)
c.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息
}
}
其中需要说明的一点是,将当前请求的userId
信息保存到请求的上下文c
上时,我们key
用了一个全局变量,而不是一个写死的字符串userId
,是因为后续其他接口需要从这个上下文中获取userId
时也要用到这个变量,所以作为全局的较好。
constdef/constdef.go
package constdef
const CtxUserIDKey = "userId"
从token
中获取用户id
一般都是controller
层入口要做的事情,可能有很多接口需要从token
获取用户id
,所以可以封装一个函数
controller/request.go
package controller
import (
"bluebell/constdef"
"errors"
"github.com/gin-gonic/gin"
)
var ErrorUserNotLogin = errors.New("用户未登录")
// getCurrentUserID 获取当前登录的用户ID
func getCurrentUserID(c *gin.Context) (userID int64, err error) {
uid, ok := c.Get(constdef.CtxUserIDKey)
if !ok {
err = ErrorUserNotLogin
return
}
userID, ok = uid.(int64)
if !ok {
err = ErrorUserNotLogin
return
}
return
}```