Herkese merhaba!

Uzun yıllardır bol miktarda kişisel zaman ve enerji harcayarak bilgimizi hepinizle paylaşıyoruz. Ancak şu andan itibaren bu blogu çalışır durumda tutabilmek için yardımınıza ihtiyacımız var. Yapmanız gereken tek şey, sitedeki reklamlardan birine tıklamak olacaktır, aksi takdirde hosting vb. masraflar nedeniyle maalesef yayından kaldırılacaktır. Teşekkürler.

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


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