Golang Gin框架使用JWT實現簡單身份驗證

使用JWT包生成token

詳情看代碼註釋

// 記得引入jwt包
// import "github.com/dgrijalva/jwt-go"

// 這裏定義的字段其實就是生成token串中的屬性
type JwtCustomClaims struct {
	jwt.StandardClaims // 包中自帶的默認屬性

	Uid  uint `json:"uid"` // 當前訪問人uid 自定義添加一些自己需要的元素
}

// 生成token串
// 這裏的參數一般爲自己自定義的一些參數
func GenerateToken(userId uint, role int8) (string, error) {
	// 這裏使用的是自定義結構體的方式牀架claims,也可以使用jwt.MapClaims結構體
	/*
		claims := jwt.MapClaims{} // 其實就是個map
		claims["uid"] = userId
	*/
	claims := JwtCustomClaims{
		jwt.StandardClaims{
			ExpiresAt: int64(time.Now().Add(time.Minute * time.Duration(expire)).Unix()),
			Issuer:    config.GetConfig().Encrypt.EncryptIssuer,
		},
		userId,
		role,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 這裏需要注意當使用jwt.SigningMethodHS256方式生成token串時,SignedString方法的參數應該是[]byte數組,其他方式也對key有着要求
	tokenStr, err := token.SignedString([]byte(config.GetConfig().Encrypt.EncryptKey))
	if err != nil {
		logrus.Errorf("[GenerateToken] generate token error,err_msg = %s", err.Error())
		return "", err
	}

	return tokenStr, nil
}

// 解析token串數據

// 解析token串
func ParseToken(tokenStr string) (cliams jwt.MapClaims, err error) {
	// 創建對象,直接調用parse方法
	var token *jwt.Token
	token, err = jwt.Parse(tokenStr, func(*jwt.Token) (interface{}, error) {
		// 注意這裏也是[]byte數組
		return []byte(config.GetConfig().Encrypt.EncryptKey), nil
	})
	if err != nil {
		logrus.Errorf("[ParseToken] parse token error,err_msg = %s", err.Error())
		return
	}

	// 獲取jwt.Token對象後,獲取定義的claims
	var ok bool
	// 注意 生成token串時無論是用自定義結構體的方式還是直接使用jwt.MapClaims,這裏斷言結果都爲jwt.MapClaims(斷言爲自定義的結構體會失敗)
	// 斷言獲取到的jwt.MapClaims實際上爲map[string]interface{},使用key獲取值時,需要再做一次類型斷言,需要注意類型的轉換,數值類型會被轉化爲float64(設置map["uid"] = 10,獲取到的map["uid"]實際爲float64類型的10.00000)
	cliams, ok = token.Claims.(jwt.MapClaims)
	if !ok {
		logrus.Errorf("[ParseToken]token claims type error,can't convert")
		return cliams, fmt.Errorf("token claims can't convert to JwtCustomerClaims")
	}

	return
}

使用Gin中間件攔截驗證token串

func TokenAuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
	// 這裏需要根據前端傳遞token傳的方式使用不同的方式獲取
	// 放在請求頭(Request Header),使用c.Request.Header.Get("[前端設置的key]") 【注意將自定義的key放在header中時,後端是否進行了跨域資源訪問的控制】
	// 放在get請求鏈接中或post請求的參數中,使用c.Query("[前端設置的key]")
		tokenStr := c.Request.Header.Get("api_token")
		if tokenStr == "" {
			// 嘗試從鏈接中獲取 兼容下部分特殊get請求
			tokenStr = c.Query("api_token")
			if tokenStr == "" {
				c.JSON(http.StatusUnauthorized, gin.H{
					"success": "false",
					"msg":     "用戶未登陸",
				})
				c.Abort()
				return
			}
		}

		// 解析token
		claims, err := token.ParseToken(tokenStr)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{
				"success": "false",
				"msg":     "token 解析失敗",
			})
			c.Abort()
			return
		}
		// 注意這裏數值類型斷言後的類型位float64
		c.Set("uid", int(claims["uid"].(float64)))
		c.Next()
	}
}

Gin跨域資源訪問控制

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method               //請求方法
		origin := c.Request.Header.Get("Origin") //請求頭部
		
		if origin != "" {
			c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Origin", "*")                                       // 這是允許訪問所有域
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服務器支持的所有跨域請求的方法,爲了避免瀏覽次請求的多次'預檢'請求
			// 瀏覽器請求時,Request Header中允許使用所有key
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,api_token")
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域關鍵設置 讓瀏覽器可以解析
			c.Header("Access-Control-Max-Age", "172800")                                                                                                                                                           // 緩存請求信息 單位爲秒
			c.Header("Access-Control-Allow-Credentials", "false")                                                                                                                                                  //  跨域請求是否需要帶cookie信息 默認設置爲true
			c.Set("content-type", "application/json")                                                                                                                                                              // 設置默認返回格式是json
		}

		//放行所有OPTIONS方法 原因爲當Request Header中存在自定義的key時,瀏覽器會首先發送一個options請求,判斷後端是否支持,只有返回狀態碼爲200時,纔會真正發送請求
		if method == "OPTIONS" {
			c.JSON(http.StatusOK, "Options Request!")
		}
		// 處理請求
		c.Next()
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章