Gin 集成: 集成 JWT
- 介紹
JWT
全稱JSON Web Token
是一種跨域認證解決方案,屬於一個開放的標準,它規定了一種Token
實現方式,目前多用於前後端分離項目和OAuth2.0
業務場景下。
jwt-go 是使用
Go
語言實現的Json web token (JWT)
, 目前GitHub Start 9.8k
, 源碼地址: https://github.com/dgrijalva/jwt-go,從版本 3.2.1 開始, 源碼地址變更爲:github.com/golang-jwt/jwt
, 需要下載最新版本時, 可以使用這個地址。
1.2 集成示意圖
- 配置
2.1 編輯主配置
文件位置:./config.yaml
app:
...
log:
...
mysql:
...
jwt:
secret: liuqh.icu # jwt生成密鑰
issuer: 猿碼記 # 簽發人
expire: 3h # 有效時間,值如: 30s|10min|1h
2.2 新增結構體
文件位置: ./config/jwt.go
/**
* @Description JWT配置
**/
package config
import "time"
// JSON WEB TOKEN 配置
type jwt struct {
Secret string `yaml:"secret"`
Issuer string `yaml:"issuer"`
Expire time.Duration `yaml:"expire"`
}
- 編輯中間件
文件位置:./middleware/jwt.go
,功能包括有中間件函數/創建Token/解析Token
3.1 中間件函數
/**
* @Description JWT中間件
**/
package middleware
import (
"52lu/go-import-template/global"
"52lu/go-import-template/model/dao"
"52lu/go-import-template/model/request"
"52lu/go-import-template/model/response"
"errors"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"go.uber.org/zap"
"net/http"
"time"
)
/**
* @description: JWT中間件
* @return func(ctx *gin.Context)
*/
func JWTAuthMiddleware() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
// 獲取參數中的token
token := getToken(ctx)
global.GvaLogger.Sugar().Infof("token: %s",token)
if token == "" {
response.Error(ctx,"Token不能爲空!")
// 中斷請求
ctx.Abort()
return
}
// 驗證Token
userClaim, err := ParseToken(token)
if err != nil {
response.ErrorWithToken(ctx,"Token error :" + err.Error())
// 中斷請求
ctx.Abort()
return
}
// 設置到上下文中
setContextData(ctx,userClaim,token)
// 繼續請求後續流程
ctx.Next()
}
}
// 設置數據到上下文
func setContextData(ctx *gin.Context,userClaim *request.UserClaims,token string) {
userDao := &dao.UserDao{
Uid: userClaim.Uid,
}
user, err := userDao.FindUser()
if err != nil {
response.Error(ctx,"用戶不存在!")
// 中斷請求
ctx.Abort()
return
}
user.Token = token
ctx.Set("userClaim",userClaim)
ctx.Set("user",user)
}
// 從請求中獲取Token
func getToken(ctx *gin.Context) string {
var token string
// 從header中獲取
token = ctx.Request.Header.Get("TOKEN")
if token != "" {
return token
}
// 獲取當前請求方法
if ctx.Request.Method == http.MethodGet {
// 從Get請求中獲取Token
token, ok := ctx.GetQuery("token")
if ok {
return token
}
}
// 從POST中和獲取
if ctx.Request.Method == http.MethodPost {
postParam := make(map[string]interface{})
_ = ctx.ShouldBindJSON(&postParam)
token, ok := postParam["token"]
if ok {
return token.(string)
}
}
return ""
}
3.2 創建 Token
// 創建Jwt
func CreateToken(uid uint) (string, error) {
newWithClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, &request.UserClaims{
StandardClaims: &jwt.StandardClaims{
ExpiresAt: time.Now().Add(global.GvaConfig.Jwt.Expire).Unix(), // 有效期
Issuer: global.GvaConfig.Jwt.Issuer, // 簽發人
IssuedAt: time.Now().Unix(), // 簽發時間
},
Uid: uid,
})
return newWithClaims.SignedString([]byte(global.GvaConfig.Jwt.Secret))
}
3.3 解析 Token
// 驗證JWT
func ParseToken(tokenString string) (*request.UserClaims, error) {
var err error
var token *jwt.Token
token, err = jwt.ParseWithClaims(tokenString, &request.UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(global.GvaConfig.Jwt.Secret), nil
})
if err != nil {
global.GvaLogger.Error("解析JWT失敗", zap.String("error", err.Error()))
return nil, err
}
// 斷言
userClaims, ok := token.Claims.(*request.UserClaims)
// 驗證
if !ok || !token.Valid {
return nil, errors.New("JWT驗證失敗")
}
return userClaims, nil
}
- 註冊路由
註冊路由流程
4.1 不需要登錄路由
1. 註冊路由
文件位置:router/user_router.go
/**
* @description: 用戶相關的路由
* @param engine
*/
func InitUserRouter(engine *gin.Engine) {
// 不需要登錄的路由
noLoginGroup := engine.Group("v1/user")
{
// 登錄
noLoginGroup.POST("login", v1.Login)
}
}
2. 路由綁定函數
文件位置:./api/v1/user_api.go
/**
* @description: TODO 用戶賬號密碼登錄
* @param ctx
*/
func Login(ctx *gin.Context) {
// 綁定參數
var loginParam request.LoginParam
_ = ctx.ShouldBindJSON(&loginParam)
//...(省略)
// 生成token
token, err := middleware.CreateToken(userRecord.ID)
if err != nil {
global.GvaLogger.Sugar().Errorf("登錄失敗,Token生成異常:%s", err)
response.Error(ctx, "登錄失敗,賬號或者密碼錯誤!")
return
}
userRecord.Token = token
response.OkWithData(ctx, userRecord)
}
3. 請求返回
4.2 需要登錄路由
1. 註冊路由
文件位置:router/user_router.go
/**
* @description: 用戶相關的路由
* @param engine
*/
func InitUserRouter(engine *gin.Engine) {
// 不需要登錄的路由
...
// 需要登錄
tokenGroup := engine.Group("v1/user").Use(middleware.JWTAuthMiddleware())
{
tokenGroup.POST("/detail", v1.GetUser)
}
}
2. 路由綁定函數
文件位置:./api/v1/user_api.go
// 查詢用戶信息
func GetUser(ctx *gin.Context) {
// 從上下文中獲取用戶信息,(經過中間件邏輯時,已經設置到上下文)
user, _ := ctx.Get("user")
response.OkWithData(ctx, user)
return
}
3. 請求返回
關注【猿碼記】微信公衆號,回覆【gin 集成】獲取源碼。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/0ayMVsTdbI3OmgKOyyWCKQ