什么是智威汤逊?JSONWebToken(简称JWT)是目前最流行的跨域认证方案,也是前后端分离项目中常用的一种认证技术。本文介绍了如何在GolangGinWeb框架中使用JWT认证中间件和参考,JWT的详细原理可以参考:JWTRFC:https://tools.ietf.org/html/rfc7519JWTIETF:http://self-issued.info/docs/draft-ietf-oauth-json-web-token.htmlJSONWebToken入门教程:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html主要流程是初始化Gin引擎,定义获取Token的接口。访问该接口,内部自动生成JWTtoken,返回给前端定义需要认证的路由接口,使用JWT中间件进行认证。中间件使用GoConvey(Golang的测试框架,集成gotest,支持终端和浏览器模式),构建customers端,填写Token,模拟前端访问JWT中间件进行鉴权,若鉴权则返回消息体通过,否则返回401或其他错误流程图该流程图描述了服务器代码中的Token结构和认证过程。在main.go中填写如下代码,运行gorunmain.go,启动web服务。packagemainimport(jwt_lib"github.com/dgrijalva/jwt-go""github.com/dgrijalva/jwt-go/request""github.com/gin-gonic/gin""log""time")var(mysupersecretpassword="unicornsAreAwesome")funcAuth(secretstring)gin.HandlerFunc{returnfunc(c*gin.Context){//log.Printf("Request:\n%+v",c.Request)//ParseFromRequest方法提取路径请求中的JWTtoken,并验证token,err:=request.ParseFromRequest(c.Request,request.OAuth2Extractor,func(token*jwt_lib.Token)(interface{},error){b:=([]byte(secret))//log.Printf("b:%+v",b)returnb,nil})log.Printf("token:%+v",token)iferr!=nil{c.AbortWithError(401,err)}}}funcmain(){r:=gin.Default()public:=r.Group("/api")//定义根路由访问http://locahost:8080/api/获取tokenpublic.GET("/",func(c*gin.Context){//CreatethetokenNew方法接受一个签名的接口类型(SigningMethod)参数method,返回一个Token结构体指针//GetSigningMethod(签名算法algorithm)token:=jwt_lib.New(jwt_lib.GetSigningMethod("HS256"))//默认签名算法为HMACSHA256(写成HS256)log.Printf("token:%+v",token)//2020/12/1022:32:02token:&{Raw:Method:0xc00000e2a0Header:map[alg:HS256typ:JWT]Claims:map[]Signature:Valid:false}//Setsomeclaims设置Id和expiration时间字段,MapClaims实现Claimms接口token.Claims=jwt_lib.MapClaims{"Id":"Christopher","exp":time.Now().Add(time.Hour*1).Unix(),}//Signandgetthecompletecodedtokenasastring//签名得到完整编码的Token串tokenString,err:=token.SignedString([]byte(mysupersecretpassword))//{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IkNocmlzdG9waGVyIiwiZXhwIjoxNjA3NjE0MzIyfQ.eQd7ztDn3706GrpitgnikKgOtzx-RHnq7cr2eqUlsZo"}iferr!=nil{c.JSON(500,gin.H{"message":"Couldnotgeneratetoken"})}c.JSON(200,gin.H{"token":tokenString})})//定义需要通过Token验证才能访问http://localhost:8080/api/privateprivate:=r.Group("/api/private")的私有接口组private.Use(Auth(mysupersecretpassword))//使用JWT认证中间件(带参数)/*Setthisheaderinyourrequesttogethere.Authorization:Bearer`token`*//定义一个具体的私有根接口:http://localhost:8080/api/private/private.GET("/",func(c*gin.Context){c.JSON(200,gin.H{"message":"Hellofromprivate"})})r.Run("localhost:8080")}客户端代码新建一个jwt_test.go文件,填写下面的代码,然后运行??gotest来执行单元测试。packagetest_testimport("encoding/json"."github.com/smartystreets/goconvey/convey"//https://github.com/smartystreets/goconveyGoConvey是Golang的测试框架,集成gotest,支持终端和浏览器模式。"io/ioutil"“log”“net/http”“strings”“testing”)typeUserstruct{Usernamestring`json:"username"`Passwordstring`json:"password"`}typeResponsestruct{Tokenstring`json:"token"`}funccreateNewsUser(用户名,密码字符串)*User{return&User{username,password}}funcTestLogin(t*testing.T){Convey("Shouldbeabletologin",t,func(){user:=createNewsUser("jonas","1234")jsondata,_:=json.Marshal(user)userData:=strings.NewReader(string(jsondata))log.Printf("userData:%+v",userData)//这里模拟用户登录,实际上并没有使用用户名和密码在后台,这个接口直接返回内部生成的Tokenreq,_:=http.NewRequest("GET","http://localhost:8080/api/",userData)req.Header.Set("Content-Type""application/json")client:=&http.Clientt{}res,_:=client.Do(req)//log.Printf("res:%+v",res)So(res.StatusCode,ShouldEqual,200)//断言响应码,期望得到状态码为200Convey("Shouldbeabletoparsebody",func(){//解析响应bodybody,err:=ioutil.ReadAll(res.Body)deferres.Body.Close()So(err,ShouldBeNil)Convey("Shouldbeabletogetjsonback",func(){responseData:=new(Response)err:=json.Unmarshal(body,responseData)So(err,ShouldBeNil)log.Printf("responseData:%s",responseData)Convey("Shouldbeabletobeauthorized",func(){token:=responseData.Token//提取Tokenlog.Printf("token:%s",token)//用Token构造请求req,_:=http.NewRequest("GET","http://localhost:8080/api/private",nil)req.Header.Set("Authorization","Bearer"+token)//设置认证头client=&http.Client{}res,_:=client.Do(req)body,err:=ioutil.ReadAll(res.Body)iferr!=nil{log.Printf("Readbodyfailed,%s",err.Error())}log.Printf("Body:%s",string(body))所以(res.StatusCode,ShouldEqual,200)})})})})}参考文档gin-gonic/contrib/jwt中间件:https://github.com/gin-gonic/contrib/tree/master/jwt
