久美 發表於 2020-3-13 16:24:00

golang jwt-go的使用

<h1 id="jwtjson-web-token">JWT(json web token)</h1>
<h3 id="jwt">jwt</h3>
<p>jwt的原理和session有点相像,其目的是为了解决rest api中无状态性</p>
<p>因为rest接口,需要权限校验。但是又不能每个请求都把用户名密码传入,因此产生了这个token的方法</p>
<p>流程:</p>
<p>https://blog.wangjunfeng.com/post/golang-jwt/#3-%E7%AD%BE%E5%90%8D-signature</p>
<ol>
<li>
<p>用户访问auth接口,获取token</p>
<p>服务器校验用户传入的用户名密码等信息,确认无误后,产生一个token。<s>这个token其实是类似于map的数据结构(jwt数据结构)中的key。</s></p>
<p>准确的应该是:token中其实就保存了用户的信息,只是被加密过了。怪不得服务器重启了token还能使用,就是这个原因,因为数据就是保存在token这条长长的字符串中的。</p>
</li>
<li>
<p>用户访问需要权限验证的接口,并传入token。</p>
<p>服务器验证token:根据自己的token密钥判断token是否正确(是否被别人篡改),正确后才从token中解析出token中的信息。可能会把解析出的信息保存在context中</p>
</li>
</ol>
<h3 id="使用步骤">使用步骤</h3>
<ol>
<li>
<p>下载依赖包</p>
<p><code>go get -u github.com/dgrijalva/jwt-go</code></p>
</li>
<li>
<p>编写jwt工具包,用户创建和检查token</p>
<p>分为几个部分:</p>
<ol>
<li>指定加密密钥</li>
<li>指定被保存在token中的实体对象,Claims 结构体。需要内嵌jwt.StandardClaims。这个结构体是用来保存信息的。</li>
<li>根据数据产生token:根据传入的信息,组装成一个Claims结构体对象,再从对象中获取token</li>
<li>根据token解析数据:解析出token所对应的interface{},再使用断言解析出Claims对象,取数据</li>
</ol>
<pre><code class="language-go">/ 指定加密密钥
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, &amp;Claims{}, func(token *jwt.Token) (interface{}, error) {
                return jwtSecret, nil
        })

        if tokenClaims!=nil{
                // 从tokenClaims中获取到Claims对象,并使用断言,将该对象转换为我们自己定义的Claims
                // 要传入指针,项目中结构体都是用指针传递,节省空间。
                if claims,ok:=tokenClaims.Claims.(*Claims);ok&amp;&amp;tokenClaims.Valid{
                        return claims,nil
                }
        }
        return nil,err

}
</code></pre>
</li>
<li>
<p>编写路由,返回token</p>
<p>(需要做用户参数校验与错误处理)</p>
<ol>
<li>用户参数校验</li>
</ol>
<pre><code class="language-go">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(&amp;a)


        //创建返回信息
        data:=make(mapinterface{})
        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))
}
</code></pre>
</li>
<li>
<p>编写中间件,校验token字符串</p>
<pre><code class="language-go">func JWY()gin.HandlerFunc{
        return func(c *gin.Context){
                varcode 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()&gt;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()
        }
}
</code></pre>
</li>
</ol>
<p>​</p><br><br>
来源:https://www.cnblogs.com/jianga/p/12487267.html
頁: [1]
查看完整版本: golang jwt-go的使用