Bu örnekte, RSA RS256 özel (private key) anahtarını kullanarak bir JWT token oluşturacağız ve bunu genel anahtarla (public key) doğrulayacağız. RSA, hem oluşturma (private) hem de doğrulama (public) için farklı anahtarlar kullanan asimetrik bir imzalama yöntemidir. Bunu, token oluşturucunun (sunucu uygulaması) ve kullanıcısının (istemci uygulaması) imza doğrulamasına izin veriliyorsa kullanın. Token yaratıcısı hem özel hem de genel anahtarları bilirken, kullanıcı yalnızca genel anahtarı bilir.


Anahtarlar


Anahtarlar genellikle verilen eylem için şu şekilde kullanılır.



Hem özel hem de genel RSA anahtarları oluşturmak için önce aşağıdaki komutu çalıştırın.


.PHONY: cert
cert:
openssl genrsa -out cert/id_rsa 4096
openssl rsa -in cert/id_rsa -pubout -out cert/id_rsa.pub

main.go


package main

import (
"fmt"
"io/ioutil"
"log"
"time"

"github.com/you/client/internal/pkg/token"
)

func main() {
prvKey, err := ioutil.ReadFile("cert/id_rsa")
if err != nil {
log.Fatalln(err)
}
pubKey, err := ioutil.ReadFile("cert/id_rsa.pub")
if err != nil {
log.Fatalln(err)
}

jwtToken := token.NewJWT(prvKey, pubKey)

// 1. Create a new JWT token.
tok, err := jwtToken.Create(time.Hour, "Can be anything")
if err != nil {
log.Fatalln(err)
}
fmt.Println("TOKEN:", tok)

// 2. Validate an existing JWT token.
content, err := jwtToken.Validate(tok)
if err != nil {
log.Fatalln(err)
}
fmt.Println("CONTENT:", content)
}

token.go


package token

import (
"fmt"
"time"

"github.com/dgrijalva/jwt-go"
)

type JWT struct {
privateKey []byte
publicKey []byte
}

func NewJWT(privateKey []byte, publicKey []byte) JWT {
return JWT{
privateKey: privateKey,
publicKey: publicKey,
}
}

func (j JWT) Create(ttl time.Duration, content interface{}) (string, error) {
key, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey)
if err != nil {
return "", fmt.Errorf("create: parse key: %w", err)
}

now := time.Now().UTC()

claims := make(jwt.MapClaims)
claims["dat"] = content // Our custom data.
claims["exp"] = now.Add(ttl).Unix() // The expiration time after which the token must be disregarded.
claims["iat"] = now.Unix() // The time at which the token was issued.
claims["nbf"] = now.Unix() // The time before which the token must be disregarded.

token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(key)
if err != nil {
return "", fmt.Errorf("create: sign token: %w", err)
}

return token, nil
}

func (j JWT) Validate(token string) (interface{}, error) {
key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
if err != nil {
return "", fmt.Errorf("validate: parse key: %w", err)
}

tok, err := jwt.Parse(token, func(jwtToken *jwt.Token) (interface{}, error) {
if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected method: %s", jwtToken.Header["alg"])
}

return key, nil
})
if err != nil {
return nil, fmt.Errorf("validate: %w", err)
}

claims, ok := tok.Claims.(jwt.MapClaims)
if !ok || !tok.Valid {
return nil, fmt.Errorf("validate: invalid")
}

return claims["dat"], nil
}

Test


Token'i deşifre etmek için jwt.io, JWT hakkında genel bilgi için JSON Web Token (JWT) adreslerini kullanabilirsiniz.


$ go run -race main.go

TOKEN: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXQiOiJDYW4gYmUgYW55dGhpbmciLCJleHAiOjE2MDQyNTI5NzUsImlhdCI6MTYwNDI0OTM3NSwibmJmIjoxNjA0MjQ5Mzc1fQ.Ig-odzhYvhBLVvbs7y2zELFC1qgLCp4EC-XrDmcxhxHxIwS1u3i3gQ8FnzI7CKa495CFgZtwjk_SJhUGyCQQmVjpMpGYrZiJJ_4QiIlC8b0bFrq65SZOqumMzSE9pUH0V-pWiIzkqWni6PX9KLS7YJvr8o7l1dJ772d5nWw8wZDVJn76PHJo7SEtInA3-l-oxDvQ2rqtefo5enkDM_2Yg77h3542KwGFZVig8B-bzl6kO8w391gXJa8GdfIsbLsXFnI1-LdWZzjXSnD3wsUV8PsBJ0AkCYccwV7i4Sk4d56XkgTcb5IHixcwm64RXkEWmxw_RLZlMz4Fa6mSSB3nTbJnYFGxV8t35KKQjrsTdaRuVfGaxw_i54JAktpJxoRioR846f1o_OsvyrHY1cDi8kPEVinuW_UiRBLD26dOEvBbreM0bQaPn9K3_a6gBwtQX0xdaQsSha4dvvPZ-krzZr3TWzkewQRCRaZqJeL0pdiPV_l95R3HUCQTrznLzKu-QlwxBELrC92NK6ex13XuovBFRHfZDnTaXFc0JcSmXLRAAL7PREcLiAZCTz5b0oVP7K_vIjf2LhqYTPgUxUYm4HZySBxPEtu5QiVZA807nEBXpTWZ4DZAlyPYXokzV1jFfDBRtYRwxA6CqW7SWUwOPFal0IxvjlV4sP5SJwqGawM

CONTENT: Can be anything