golang jwt-go的使用


JWT(json web token)

jwt

jwt的原理和session有點相像,其目的是為了解決rest api中無狀態性

因為rest接口,需要權限校驗。但是又不能每個請求都把用戶名密碼傳入,因此產生了這個token的方法

流程:

https://blog.wangjunfeng.com/post/golang-jwt/#3-%E7%AD%BE%E5%90%8D-signature

  1. 用戶訪問auth接口,獲取token

    服務器校驗用戶傳入的用戶名密碼等信息,確認無誤后,產生一個token。這個token其實是類似於map的數據結構(jwt數據結構)中的key。

    准確的應該是:token中其實就保存了用戶的信息,只是被加密過了。怪不得服務器重啟了token還能使用,就是這個原因,因為數據就是保存在token這條長長的字符串中的。

  2. 用戶訪問需要權限驗證的接口,並傳入token。

    服務器驗證token:根據自己的token密鑰判斷token是否正確(是否被別人篡改),正確后才從token中解析出token中的信息。可能會把解析出的信息保存在context中

使用步驟

  1. 下載依賴包

    go get -u github.com/dgrijalva/jwt-go

  2. 編寫jwt工具包,用戶創建和檢查token

    分為幾個部分:

    1. 指定加密密鑰
    2. 指定被保存在token中的實體對象,Claims 結構體。需要內嵌jwt.StandardClaims。這個結構體是用來保存信息的。
    3. 根據數據產生token:根據傳入的信息,組裝成一個Claims結構體對象,再從對象中獲取token
    4. 根據token解析數據:解析出token所對應的interface{},再使用斷言解析出Claims對象,取數據
    / 指定加密密鑰
    var jwtSecret=[]byte(setting.JwtSecret)
    
    //Claim是一些實體(通常指的用戶)的狀態和額外的元數據
    type Claims struct{
    	Username string `json:"username"`
    	Password string `json:"password"`
    	jwt.StandardClaims
    }
    
    // 根據用戶的用戶名和密碼產生token
    func GenerateToken(username ,password string)(string,error){
    	//設置token有效時間
    	nowTime:=time.Now()
    	expireTime:=nowTime.Add(3*time.Hour)
    
    	claims:=Claims{
    		Username:       username,
    		Password:       password,
    		StandardClaims: jwt.StandardClaims{
                // 過期時間
    			ExpiresAt:expireTime.Unix(),
    			// 指定token發行人
    			Issuer:"gin-blog",
    		},
    	}
    
    	tokenClaims:=jwt.NewWithClaims(jwt.SigningMethodHS256,claims)
    	//該方法內部生成簽名字符串,再用於獲取完整、已簽名的token
    	token,err:=tokenClaims.SignedString(jwtSecret)
    	return token,err
    }
    
    // 根據傳入的token值獲取到Claims對象信息,(進而獲取其中的用戶名和密碼)
    func ParseToken(token string)(*Claims,error){
    
    	//用於解析鑒權的聲明,方法內部主要是具體的解碼和校驗的過程,最終返回*Token
    	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
    		return jwtSecret, nil
    	})
    
    	if tokenClaims!=nil{
    		// 從tokenClaims中獲取到Claims對象,並使用斷言,將該對象轉換為我們自己定義的Claims
    		// 要傳入指針,項目中結構體都是用指針傳遞,節省空間。
    		if claims,ok:=tokenClaims.Claims.(*Claims);ok&&tokenClaims.Valid{
    			return claims,nil
    		}
    	}
    	return nil,err
    
    }
    
  3. 編寫路由,返回token

    (需要做用戶參數校驗與錯誤處理)

    1. 用戶參數校驗
    type auth struct{
    	Username string `valid:"Required;MaxSize(50)"`
    	Password string `valid:"Required;MaxSize(50)"`
    }
    
    func GetAuth(c *gin.Context){
    	username:=c.Query("username")
    	password:=c.Query("password")
    
    	valid:=validation.Validation{}
    	a:=auth{
    		Username: username,
    		Password: password,
    	}
    
    
    	// 與之前的對每一個數據分開驗證不同,此處在auth對象中通過定義標簽valid
    	// 一次性校驗對象中的所有字段信息
    	ok,_:=valid.Valid(&a)
    
    
    	//創建返回信息
    	data:=make(map[string]interface{})
    	code:=e.INVALID_PARAMS
    	/*
    	根據用戶名密碼獲取token 判斷流程:
    	1. 先判斷用戶名密碼是否存在
    	*/
    	if ok{
    		isExist:=models.CheckAuth(username,password)
    		if isExist{
    			token,err:=util.GenerateToken(username,password)
    			if err!=nil{
    				code=e.ERROR_AUTH_TOKEN
    			}else{
    				data["token"]=token
    				code=e.SUCCESS
    			}
    		}else{
    			code=e.ERROR_AUTH
    		}
    	}else{
    		//如果數據驗證失敗,則打印結果
    		for _,err:=range valid.Errors{
    			log.Println(err.Key,err.Message)
    		}
    	}
    
    	c.JSON(http.StatusOK,util.ReturnData(code,e.GetMsg(code),data))
    }
    
  4. 編寫中間件,校驗token字符串

    func JWY()gin.HandlerFunc{
    	return func(c *gin.Context){
    		var  code int
    		var data interface{}
    
    		code=e.SUCCESS
    		token:=c.Query("token")
    		if token==""{
    			code=e.ERROR_AUTH_NO_TOKRN
    		}else{
    			claims,err:=util.ParseToken(token)
    			if err!=nil{
    				code=e.ERROR_AUTH_CHECK_TOKEN_FAIL
    			}else if time.Now().Unix()>claims.ExpiresAt{
    				code=e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
    			}
    		}
    
    		//如果token驗證不通過,直接終止程序,c.Abort()
    		if code!=e.SUCCESS{
    			// 返回錯誤信息
    			c.JSON(http.StatusUnauthorized,util.ReturnData(code,e.GetMsg(code),data))
    			//終止程序
    			c.Abort()
    			return
    		}
    		c.Next()
    	}
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM