go-zero jwt鑒權(quán)

2023-02-23 15:29 更新

概述

JSON Web令牌(JWT)是一個(gè)開(kāi)放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊而獨(dú)立的方法,用于在各方之間安全地將信息作為JSON對(duì)象傳輸。由于此信息是經(jīng)過(guò)數(shù)字簽名的,因此可以被驗(yàn)證和信任??梢允褂妹孛埽ㄊ褂肏MAC算法)或使用RSA或ECDSA的公鑰/私鑰對(duì)對(duì)JWT進(jìn)行簽名。

什么時(shí)候應(yīng)該使用JWT

  • 授權(quán):這是使用JWT的最常見(jiàn)方案。一旦用戶(hù)登錄,每個(gè)后續(xù)請(qǐng)求將包括JWT,從而允許用戶(hù)訪問(wèn)該令牌允許的路由,服務(wù)和資源。單一登錄是當(dāng)今廣泛使用JWT的一項(xiàng)功能,因?yàn)樗拈_(kāi)銷(xiāo)很小并且可以在不同的域中輕松使用。
  • 信息交換:JSON Web令牌是在各方之間安全地傳輸信息的一種好方法。因?yàn)榭梢詫?duì)JWT進(jìn)行簽名(例如,使用公鑰/私鑰對(duì)),所以您可以確保發(fā)件人是他們所說(shuō)的人。此外,由于簽名是使用標(biāo)頭和有效負(fù)載計(jì)算的,因此您還可以驗(yàn)證內(nèi)容是否未被篡改。

為什么要使用JSON Web令牌

由于JSON不如XML冗長(zhǎng),因此在編碼時(shí)JSON的大小也較小,從而使JWT比SAML更為緊湊。這使得JWT是在HTML和HTTP環(huán)境中傳遞的不錯(cuò)的選擇。

在安全方面,只能使用HMAC算法由共享機(jī)密對(duì)SWT進(jìn)行對(duì)稱(chēng)簽名。但是,JWT和SAML令牌可以使用X.509證書(shū)形式的公用/專(zhuān)用密鑰對(duì)進(jìn)行簽名。與簽署JSON的簡(jiǎn)單性相比,使用XML Digital Signature簽署XML而不引入模糊的安全漏洞是非常困難的。

JSON解析器在大多數(shù)編程語(yǔ)言中都很常見(jiàn),因?yàn)樗鼈冎苯佑成涞綄?duì)象。相反,XML沒(méi)有自然的文檔到對(duì)象的映射。與SAML斷言相比,這使使用JWT更加容易。

關(guān)于用法,JWT是在Internet規(guī)模上使用的。這突顯了在多個(gè)平臺(tái)(尤其是移動(dòng)平臺(tái))上對(duì)JSON Web令牌進(jìn)行客戶(hù)端處理的簡(jiǎn)便性。

go-zero中怎么使用jwt

jwt鑒權(quán)一般在api層使用,我們這次演示工程中分別在user api登錄時(shí)生成jwt token,在search api查詢(xún)圖書(shū)時(shí)驗(yàn)證用戶(hù)jwt token兩步來(lái)實(shí)現(xiàn)。

user api生成jwt token

接著業(yè)務(wù)編碼章節(jié)的內(nèi)容,我們完善上一節(jié)遺留的getJwtToken方法,即生成jwt token邏輯

添加配置定義和yaml配置項(xiàng)

$ vim service/user/api/internal/config/config.go
type Config struct {
    rest.RestConf
    Mysql struct{
        DataSource string
    }
    CacheRedis cache.CacheConf
    Auth      struct {
        AccessSecret string
        AccessExpire int64
    }
}
$ vim service/user/api/etc/user-api.yaml
Name: user-api
Host: 0.0.0.0
Port: 8888
Mysql:
  DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
CacheRedis:
  - Host: $host
    Pass: $pass
    Type: node
Auth:
  AccessSecret: $AccessSecret
  AccessExpire: $AccessExpire
  • $AccessSecret:生成jwt token的密鑰,最簡(jiǎn)單的方式可以使用一個(gè)uuid值。
  • $AccessExpire:jwt token有效期,單位:秒
$ vim service/user/api/internal/logic/loginlogic.go
func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) {
  claims := make(jwt.MapClaims)
  claims["exp"] = iat + seconds
  claims["iat"] = iat
  claims["userId"] = userId
  token := jwt.New(jwt.SigningMethodHS256)
  token.Claims = claims
  return token.SignedString([]byte(secretKey))
}

search api使用jwt token鑒權(quán)

編寫(xiě)search.api文件

$ vim service/search/api/search.api
type (
    SearchReq {
        // 圖書(shū)名稱(chēng)
        Name string `form:"name"`
    }

    SearchReply {
        Name string `json:"name"`
        Count int `json:"count"`
    }
)

@server(
    jwt: Auth
)
service search-api {
    @handler search
    get /search/do (SearchReq) returns (SearchReply)
}

service search-api {
    @handler ping
    get /search/ping
}
  • jwt: Auth:開(kāi)啟jwt鑒權(quán)
  • 如果路由需要jwt鑒權(quán),則需要在service上方聲明此語(yǔ)法標(biāo)志,如上文中的/search/do
  • 不需要jwt鑒權(quán)的路由就無(wú)需聲明,如上文中/search/ping

生成代碼

前面已經(jīng)描述過(guò)有三種方式去生成代碼,這里就不贅述了。

添加yaml配置項(xiàng)

$ vim service/search/api/etc/search-api.yaml
Name: search-api
Host: 0.0.0.0
Port: 8889
Auth:
  AccessSecret: $AccessSecret
  AccessExpire: $AccessExpire
  • $AccessSecret:這個(gè)值必須要和user api中聲明的一致。
  • $AccessExpire: 有效期
這里修改一下端口,避免和user api端口8888沖突

驗(yàn)證 jwt token

  • 啟動(dòng)user api服務(wù),登錄

  $ cd service/user/api
  $ go run user.go -f etc/user-api.yaml
  Starting server at 0.0.0.0:8888...
  $ curl -i -X POST \
    http://127.0.0.1:8888/user/login \
    -H 'Content-Type: application/json' \
    -d '{
      "username":"666",
      "password":"123456"
  }'

如果是在Windows的CMD里運(yùn)行,命令格式如下:

  curl -i -X POST http://127.0.0.1:8888/user/login -H "Content-Type: application/json" -d "{ \"username\":\"666\", \"password\":\"123456\" }"

訪問(wèn)結(jié)果:

  HTTP/1.1 200 OK
  Content-Type: application/json
  Date: Mon, 08 Feb 2021 10:37:54 GMT
  Content-Length: 251

  {"id":1,"name":"小明","gender":"男","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80","accessExpire":1612867074,"refreshAfter":1612823874}

  • 啟動(dòng)search api服務(wù),調(diào)用/search/do驗(yàn)證jwt鑒權(quán)是否通過(guò)

  $ go run search.go -f etc/search-api.yaml
  Starting server at 0.0.0.0:8889...

我們先不傳jwt token,看看結(jié)果

  $ curl -i -X GET \
    'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0'
  HTTP/1.1 401 Unauthorized
  Date: Mon, 08 Feb 2021 10:41:57 GMT
  Content-Length: 0

很明顯,jwt鑒權(quán)失敗了,返回401的statusCode,接下來(lái)我們帶一下jwt token(即用戶(hù)登錄返回的accessToken)

  $ curl -i -X GET \
    'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \
    -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80'
  HTTP/1.1 200 OK
  Content-Type: application/json
  Date: Mon, 08 Feb 2021 10:44:45 GMT
  Content-Length: 21

  {"name":"","count":0}

至此,jwt從生成到使用就演示完成了,jwt token的鑒權(quán)是go-zero內(nèi)部已經(jīng)封裝了,你只需在api文件中定義服務(wù)時(shí)簡(jiǎn)單的聲明一下即可。

獲取jwt token中攜帶的信息

go-zero從jwt token解析后會(huì)將用戶(hù)生成token時(shí)傳入的kv原封不動(dòng)的放在http.Request的Context中,因此我們可以通過(guò)Context就可以拿到你想要的值

$ vim /service/search/api/internal/logic/searchlogic.go

添加一個(gè)log來(lái)輸出從jwt解析出來(lái)的userId。

func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) {
    logx.Infof("userId: %v",l.ctx.Value("userId"))// 這里的key和生成jwt token時(shí)傳入的key一致
    return &types.SearchReply{}, nil
}

運(yùn)行結(jié)果

{"@timestamp":"2021-02-09T10:29:09.399+08","level":"info","content":"userId: 1"}


以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)