JWT'ye Giriş
JWT (JSON Web Token), web üzerinde kimlik doğrulaması için kısa, kendi kendini içeren bir yöntemdir. Üç bölümden oluşur: başlık (header), yük (payload) ve imza (signature). Başlık, token türü ve şifreleme algoritma bilgisini içerir, yük genellikle bazı izin bilgilerini ve kullanıcı kimliğini içeren veriyi taşır, ve imza ise token'ın bütünlüğünü ve geçerliliğini doğrulamak için kullanılır.
JWT'nin başlıca kullanım alanları şunlardır:
- Kimlik doğrulama: Kullanıcı giriş yaptıktan sonra her bir sonraki istek bir JWT içerecektir, bu sayede kullanıcının izin verilen rotalara, servislere ve kaynaklara erişmesine olanak tanır.
- Bilgi alışverişi: JWT, bilgilerin güvenli bir şekilde parti arasında iletilmesi için iyi bir yöntemdir; çünkü dijital olarak imzalanabilir, örneğin genel/özel anahtar çiftleri kullanılarak.
JWT Veri Formatı
JWT, taraflar arasında alışveriş edilecek bilgileri temsil etmek için kısa, URL-güvenli bir yoldur. Bir JWT aslında üç bölümden oluşur ve bu bölümler noktalarla (.``) ayrılır:
Başlık.Yük.İmza`. Şimdi, bu üç bölümün veri formatlarını detaylı olarak inceleyeceğiz.
1. Başlık
Başlık genellikle iki kısımdan oluşur: token türü—genellikle JWT
, ve kullanılan imza veya şifreleme algoritması, örneğin HMAC SHA256
veya RSA
. Başlık JSON formatında temsil edilir ve ardından Base64Url kullanılarak bir dizeye kodlanır. Örneğin:
{
"alg": "HS256",
"typ": "JWT"
}
Kodlamadan sonra elde edeceğiniz bir dize şuna benzer olabilir:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. Yük
Yük, genellikle bazı talepleri (genellikle bir kullanıcı) ve diğer verileri içeren bir dizi beyandan (claims) oluşur. Yük, çoklu önceden tanımlanmış beyanlar (Registered Claims) ve özel beyanlar (Private Claims) içerebilir.
Önceden tanımlanmış beyanlar şunları içerebilir:
-
iss
(Issuer): İhraç Eden -
exp
(Expiration Time): Son Kullanma Zamanı -
sub
(Subject): Konu -
aud
(Audience): Dinleyici -
iat
(Issued At): İhraç Tarihi -
nbf
(Not Before): İtibaren -
jti
(JWT ID): JWT'nin Benzersiz Kimliği
Aşağıdaki gibi JSON formatında bir örnek yük olabilir:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
Bu bilgi ayrıca Base64Url ile kodlanacak ve elde edeceğiniz bir dize şuna benzer olabilir:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0
3. İmza
İmza bölümü, iletim sırasında mesajın değiştirilmediğini doğrulamak için, yukarıda adı geçen iki kodlanmış dizeyi imzalamak için kullanılır. İlk olarak, bir anahtarı belirtmeniz gerekebilir (HMAC SHA256 algoritması kullanılıyorsa), ve ardından başlıkta belirtilen algoritmayı kullanarak başlık ve yükü imzalarsınız.
Örneğin, aşağıdaki başlık ve yük varsa:
KodlanmışBaşlık.KodlanmışYük
Onları bir anahtar kullanarak imzalamak için bir sahte kod parçası şöyle olabilir:
HMACSHA256(base64UrlEncode(başlık) + "." + base64UrlEncode(yük), gizliAnahtar)
Sonuç olarak elde edeceğiniz imzalanmış dize şuna benzer olabilir:
dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
Tamamlanmış JWT
Bu üç bölümü bir nokta (.) ile birleştirerek tam bir JWT oluşturulur:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
JWT standardı oldukça esnek olup, paylaşımdaki JWT boyutunu azaltmak için ve güvenlik nedenleriyle hassas bilgilerin depolanmaması amacıyla, yükte gereksinim duyduğunuz herhangi bir bilgiyi depolamanıza imkan tanır, ve bu bilgilerin bütünlüğünü imza yoluyla sağlar.
Go JWT kütüphanesini yüklemek
Go topluluğu JWT'yi işlemek için github.com/golang-jwt/jwt
paketi sunar. Bu kütüphaneyi yüklemek çok basittir, projenizin dizininde aşağıdaki komutu çalıştırmanız yeterlidir:
go get -u github.com/golang-jwt/jwt/v5
Yükledikten sonra, aşağıdaki gibi import
içine dahil edebilirsiniz:
import "github.com/golang-jwt/jwt/v5"
Basit bir token oluşturma
Go dilini ve HS256 algoritmasını kullanarak basit bir JWT tokenı oluşturmak için aşağıdaki adımları izlemeniz gerekmektedir:
İlk olarak, HS256 algoritmasını kullanarak yeni bir Token nesnesi oluşturun:
token := jwt.New(jwt.SigningMethodHS256)
Daha sonra, bu tokenın string temsilini oluşturmak için SignedString
metodunu kullanarak, imzalama için kullanacağınız anahtarı ileterek:
var mySigningKey = []byte("your-256-bit-secret")
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("Bir hata oluştu: %v", err)
}
fmt.Println(strToken)
Bu, herhangi bir iddia olmadan basit bir token oluşturacaktır.
Parametreleri olan bir token oluşturma
JWT'nin ana işlevlerinden biri bilgi taşımaktır. Bu bilgi, iddialar aracılığıyla Token içerisinde kodlanmıştır. Örneğin, özel iddialar oluşturalım:
// Özel İddia yapısı
type MyClaims struct {
jwt.RegisteredClaims
Username string `json:"username"`
Admin bool `json:"admin"`
}
// Özel iddiaları içeren bir token oluşturma
claims := MyClaims{
RegisteredClaims: jwt.RegisteredClaims{},
Username: "benim_kullanıcı_adım",
Admin: true,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// İmzalama için gizli anahtar
var mySigningKey = []byte("your-256-bit-secret")
// Tokenı string formatında oluşturma
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("Hata oluştu: %v", err)
}
fmt.Println(strToken)
Yukarıdaki kodda, MyClaims
yapısını kayıtlı iddiaları içerecek şekilde ve bazı özel bilgileri içerecek şekilde tanımladık. Daha sonra yine SignedString
metodunu kullanarak tokenın string temsilini oluşturuyoruz.
Tokenın ayrıştırılması ve doğrulanması
JWT'nin ayrıştırılması ve doğrulanması çok önemlidir ve aşağıdaki gibi yapabilirsiniz:
// Aynı MyClaims yapısını kullanacağız
tokenString := "your-JWT-string"
// tokenString'i ayrıştırmak için jwt paketinin kullanacağı bir fonksiyon tanımlamamız gerekmektedir
keyFunc := func(t *jwt.Token) (interface{}, error) {
// Beklenen imzalama yönteminin kullanıldığını doğrulayın
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Beklenmeyen imzalama yöntemi: %v", t.Header["alg"])
}
// jwt token için kullanılan imzalama anahtarını, imzalama sırasında kullanılan anahtarla uyumlu şekilde []byte formatında döndürün
return mySigningKey, nil
}
// Tokenı ayrıştırma
claims := &MyClaims{}
parsedToken, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
log.Fatalf("Ayrıştırma hatası: %v", err)
}
if !parsedToken.Valid {
log.Fatalf("Geçersiz Token")
}
// Bu aşamada parsedToken doğrulandı ve iddiaları okuyabiliriz
fmt.Printf("Kullanıcı: %s, Yönetici: %v\n", claims.Username, claims.Admin)
Yukarıdaki kodda, token stringi, MyClaims
örneği ve keyFunc
'i jwt.ParseWithClaims
fonksiyonuna ilettik. Bu fonksiyon imzayı doğrular ve tokenı ayrıştırarak, token geçerli ise claims
değişkenini dolduracaktır.