Ders 13 / 19
Kimlik Doğrulama ve Yetkilendirme
Yetkilendirme kararı bir açıklamadan mı geliyor, kabın varsayılanından mı, yoksa kaynakta yazılı bir denetim ifadesinden mi — filtre zinciri modellenerek üçü ayrılır; hiçbir kural taşımayan bir uç noktanın açık mı kapalı mı olduğunun, kabın kurulması sırasında verilen tek bir parametreyle belirlendiği ve bu farkın kaynakta hiç görünmediği koşturularak gösterilir.
İçindekiler
Önceki konunun son dersi işlem sınırının kaynakta nasıl açıldığını ölçtü: sınırı bir açıklama koyuyordu, ve bileşenin kendi içinden yaptığı çağrı bu sınırı hiç görmüyordu — vekil aradan çıkınca açıklama da aradan çıkıyordu. Bu konu aynı soruyu güvenlik, test ve dağıtık kurulum için soruyor; ilk durak yetkilendirme. Bir uç noktaya kimin erişebileceğini kaynakta arayan biri çoğu zaman hiçbir şey bulmaz: ne bir denetim satırı, ne bir rol karşılaştırması. Karar ya bir açıklamadan gelir ya da hiçbir yerden — nesne kabının kendi varsayılanından. Kimlik doğrulamanın ve yetkilendirmenin kavramları — kimin kim olduğu, hangi yetkinin nasıl temsil edildiği, rol ile izin arasındaki fark — M16/K04 Kimlik Doğrulama ve Yetkilendirme ile M24/K04 Kimlik, Erişim ve Kriptografi kurslarında kuruldu ve burada tekrarlanmıyor; bu ders yalnız kararın hangi düzenekle verildiğini ölçüyor. K06’nın sorduğu soru bu konuda da değişmiyor: bu davranışı kaynakta bir çağrı mı, bir açıklama mı doğurdu, yoksa hiçbiri mi? Bir filtre zinciri modelleniyor ve üç kaynak ayrılıyor: çağrı, açıklama, varsayılan. En tehlikelisi üçüncüsü, çünkü kabın varsayılanı kaynakta hiçbir iz bırakmıyor; aynı kaynak metni iki farklı varsayılanla iki farklı güvenlik duruşu veriyor ve bu ders bunu koşturarak gösteriyor.
Bu dersin sınırları da baştan çiziliyor: kimlik doğrulama (çağıranın kim olduğunu kanıtlaması) ile yetkilendirme (kanıtlanmış kimliğin neyi yapabileceğine karar verilmesi) arasındaki ayrım M16/K04’te kuruldu; bu ders çağıranın kimliğini zaten çözülmüş bir rol dizesi olarak alıyor ve yalnız yetkilendirme kararının kaynağını ölçüyor — kimliğin nasıl kanıtlandığı sıradaki dersin konusu. Gerçek bir soket, gerçek bir istek gövdesi ve gerçek bir oturum deposu yok; filtre zinciri ve kararı okuyan nesne kabı tek başına modelleniyor.
Filtre Zincirinin Uyguladığı Karar
Modellenen nesne kabı, @UcNokta işaretli yöntemleri bir yol adına eşleyen küçük bir sınıf;
bu, framework-core konusundaki nesne kabıyla aynı düzenek — orada nesneyi kuran açıklamalar
ölçüldü, burada aynı kabın bir isteği hangi kurala göre geçirdiği ölçülüyor. Bekci kurulurken
hedef nesnenin bütün yöntemlerini bir kez tarıyor ve @UcNokta taşıyanları bir yol adına
eşliyor; bu tarama kuruluş anında bir kez oluyor, her çağrıda yeniden yapılmıyor. Çağrı
geldiğinde tek halkalı bir filtre zinciri devreye giriyor: yöntemde @Yetki işareti varsa
çağıranın rolünü işaretteki rolle karşılaştırıyor; işaret yoksa kabın kurulurken verilen
varsayilanAcikMi alanına bakıyor. Gerçek bir ağ isteği, gerçek bir oturum ya da gerçek bir
sunucu yok — zincirin kendisi ve kararı okuyan yansıma var.
// Bekci.java — istegi filtre zincirinden gecirip yetki kararini kaynaktaki isarete ya da kabin varsayilanina gore veren kucuk bir nesne kabi (yardimci kaynak, main yok)
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface UcNokta { String yol(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface Yetki { String rol(); }
class Bekci {
final Object hedef;
final boolean varsayilanAcikMi;
final Map<String, Method> ucNoktalar = new LinkedHashMap<>();
Bekci(Object hedef, boolean varsayilanAcikMi) {
this.hedef = hedef;
this.varsayilanAcikMi = varsayilanAcikMi;
for (Method m : hedef.getClass().getDeclaredMethods()) {
UcNokta u = m.getAnnotation(UcNokta.class);
if (u != null) ucNoktalar.put(u.yol(), m);
}
}
// filtre zincirinin tek halkasi: aciklama varsa onu okur, yoksa kabin varsayilanina doner.
boolean izinVer(String yol, String cagiranRolu) {
Method m = ucNoktalar.get(yol);
if (m == null) return false;
Yetki y = m.getAnnotation(Yetki.class);
if (y == null) return varsayilanAcikMi;
if (y.rol().equals("herkes")) return true;
return y.rol().equals(cagiranRolu);
}
String cagir(String yol, String cagiranRolu) throws Exception {
if (!izinVer(yol, cagiranRolu)) return "engellendi";
Method m = ucNoktalar.get(yol);
m.setAccessible(true);
return (String) m.invoke(hedef);
}
static void gozle(String davranis, String kaynak, Object sonuc) {
System.out.printf("%-40s %-11s: %s%n", davranis, kaynak, sonuc);
}
}
class Uclar {
@UcNokta(yol = "/panel") @Yetki(rol = "yonetici")
String panel() { return "panel-verisi"; }
@UcNokta(yol = "/durum")
String durum() { return "calisiyor"; }
@UcNokta(yol = "/surum") @Yetki(rol = "herkes")
String surum() { return "surum-3"; }
}
Uclar sınıfının kaynağında izinVer yöntemini çağıran, ya da rolleri karşılaştıran hiçbir
satır yok. panel yönteminin üzerinde duran @Yetki(rol = "yonetici") yalnız bir işaret;
onu okuyan ve uygulayan taraf Bekci. Açıklama tipleri @Retention(RetentionPolicy.RUNTIME)
ile işaretli, çünkü kararı derleyici değil çalışma zamanında yansıma veriyor — derleme
sınıf yolundan silinen bir açıklama, Bekci’nin okuyacağı hiçbir şey bırakmaz; hedef
ElementType.METHOD olduğu için işaret yalnız yöntemlere konabiliyor, sınıfın kendisine ya
da alanlarına taşınamıyor. GT1. Filtre zincirinin tek halkası yetki açıklamasını okuyup
çağıranın rolüyle karşılaştırır; açıklama yoksa kabın kendi varsayılanına döner. GT2.
Yetki açıklaması taşıyan bir uç noktada kararı kaynakta hiçbir çağrı ifadesi doğurmaz — karar
açıklamadan gelir.
// FiltreKarari.java — yetki aciklamasi olan bir uc noktada karar, cagiranin rolunu aciklamayla karsilastiran filtre zincirinden geliyor
public class FiltreKarari {
public static void main(String[] args) throws Exception {
Bekci bekci = new Bekci(new Uclar(), true);
Bekci.gozle("panel, cagiran yonetici", "aciklama", bekci.cagir("/panel", "yonetici"));
Bekci.gozle("panel, cagiran calisan", "aciklama", bekci.cagir("/panel", "calisan"));
}
}
panel, cagiran yonetici aciklama : panel-verisi panel, cagiran calisan aciklama : engellendi
Aynı bekci nesnesi, aynı cagir çağrısı, aynı /panel yolu — değişen tek şey argüman
olarak verilen rol. Yönetici rolüyle gelen çağrı yönteme ulaşıyor ve gövdesini döndürüyor;
çalışan rolüyle gelen çağrı yöntemi hiç çalıştırmadan engellendi sonucunu alıyor. Karar
panel yönteminin gövdesinde değil, üzerindeki işarette duruyor ve işareti okuyan taraf
Bekci’nin kendisi; panel yöntemi kendi başına ne yönetici sözcüğünü tanır ne kimin
çağırdığını sorar. Yöntem yeniden yazılmadan, üzerindeki işaret değiştirilerek — örneğin
rol = "calisan" yapılarak — aynı davranış farklı bir role açılabilir; bu değişiklik gövdeye
hiç dokunmaz.
Yol eşlemesi Bekci kurulurken bir kez çıkarılıyor; cagir her seferinde yeniden yansıma
taraması yapmıyor, önceden çıkarılmış ucNoktalar haritasına bakıyor. Tanınmayan bir yol
istendiğinde izinVer doğrudan false döner — bu da bir çağrı değil, kabın kendi kuralı:
haritada karşılığı olmayan her yol, hiçbir açıklama okunmadan reddedilir. Yani üçüncü bir
varsayılan daha var, ama bu kez yönü sabit: bilinmeyen yol her koşulda kapalı, varsayilanAcikMi
alanından bağımsız. Bilinen ile bilinmeyen yol arasındaki bu asimetri kaynakta da görünmüyor;
yalnızca ucNoktalar haritasının içeriğine bakarak anlaşılabiliyor.
Kural Yazılmadığında: Varsayılanın Yönü
Uclar sınıfındaki durum yönteminin üzerinde hiçbir yetki işareti yok. Bu, kaynağı okuyan
birinin hiçbir şey bulamayacağı durum — ve tam da bu yüzden en sınırlayıcı ölçüm burada.
izinVer yöntemi işaret bulamadığında varsayilanAcikMi alanına bakıyor; bu alan Bekci
kurulurken verilen bir kurucu parametresi, kaynağın hiçbir yerinde görünmeyen bir değer.
İki kurulum arasındaki fark yalnız bu tek boolean değeri: hiçbir sınıf yeniden yazılmıyor,
hiçbir açıklama eklenmiyor.
// VarsayilanYonu.java — kaynagi degismeyen bir uc nokta, kabin kurulurken verilen tek bir parametreyle iki ayri sonuc uretiyor
public class VarsayilanYonu {
public static void main(String[] args) throws Exception {
Bekci acikBekci = new Bekci(new Uclar(), true);
Bekci kapaliBekci = new Bekci(new Uclar(), false);
Bekci.gozle("durum, acik-kurulumda", "varsayilan", acikBekci.cagir("/durum", "misafir"));
Bekci.gozle("durum, kapali-kurulumda", "varsayilan", kapaliBekci.cagir("/durum", "misafir"));
System.out.println("kaynak birebir ayni: Uclar.durum() hicbir yetki isareti tasimiyor");
}
}
durum, acik-kurulumda varsayilan : calisiyor durum, kapali-kurulumda varsayilan : engellendi kaynak birebir ayni: Uclar.durum() hicbir yetki isareti tasimiyor
Uclar.java dosyası iki koşum arasında tek bir karakter değişmedi; acikBekci ile
kapaliBekci aynı sınıftan kuruldu. Buna rağmen misafir rolüyle yapılan aynı çağrı bir
kurulumda veriyi döndürüyor, ötekinde reddediliyor. GT3. Yetki açıklaması taşımayan bir
uç noktada karar kabın kendi varsayılanından gelir; kaynak metni bu iki yönden hiçbirini
göstermez. GT4. Aynı kaynak, yeniden derlenmeden, yalnız kabın kurulması sırasında
verilen bir kurucu parametresiyle iki farklı güvenlik duruşu üretebilir — bu, dersin
sınırlayıcı ölçümü. İşaretlenmemiş bir uç nokta güvenli değildir ve güvensiz de değildir;
hangisi olduğu, kaynağı okuyarak değil, kabın hangi varsayılanla kurulduğunu bilerek
anlaşılır. Bu, dersin en önemli gözlemi: iki uç nokta arasındaki farkı “açık” ile “kapalı”
gibi sabit bir sıfatla anmak yanıltıcı, çünkü sıfat kaynağa değil kurulum kararına ait.
Bu karar Uclar sınıfını yazan tarafa da ait değil; Bekci’yi kim kuruyorsa varsayılan onun
elinde. Çok sayıda uç noktanın bulunduğu bir sınıfta yetki açıklaması unutulan tek bir yöntem,
varsayılan açık bir kurulumda sessizce erişilebilir kalır — kaynakta hiçbir hata, hiçbir
uyarı yok, çünkü unutulan bir işaret zaten hiçbir iz bırakmaz. Varsayılan kapalı bir kurulumda
aynı unutkanlık tam tersi yönde çalışır: işaretlenmemiş her yöntem erişilmez kalır, ve
eksikliği ancak çağıran tarafın “beklenmedik biçimde engellendi” gözlemiyle fark edilir. İki
yön de aynı boşluğu dolduruyor, ama doldurduğu sonuç taban tabana zıt; hangi yönün seçildiği
kabı kuran satırda bir kez yazılıyor ve bütün işaretlenmemiş yüzeyi aynı anda kapsıyor.
Varsayılanın yönü kaynakta görünmüyorsa, açıklama onu bastırabilir mi? surum
yönteminin üzerindeki @Yetki(rol = "herkes") tam bunu deniyor: özel bir rol adı değil, bir
açık kapı işareti.
// AcikcaBelirtilmis.java — herkese-acik isareti tasiyan bir uc nokta, kabin varsayilani kapali olsa bile aciklamadan geciyor
public class AcikcaBelirtilmis {
public static void main(String[] args) throws Exception {
Bekci kapaliBekci = new Bekci(new Uclar(), false);
Bekci.gozle("surum, kapali-kurulumda", "aciklama", kapaliBekci.cagir("/surum", "misafir"));
Bekci.gozle("durum, kapali-kurulumda", "varsayilan", kapaliBekci.cagir("/durum", "misafir"));
}
}
surum, kapali-kurulumda aciklama : surum-3 durum, kapali-kurulumda varsayilan : engellendi
Aynı kapaliBekci nesnesi üzerinde iki çağrı: surum açıklamayla herkese açık ilan edildiği
için varsayılanı bastırıp geçiyor; durum hiçbir işaret taşımadığı için aynı kabın
kapalı varsayılanına düşüyor. Açıklama varsa, varsayılan hiç devreye girmiyor — sıra ancak
açıklama yoksa varsayılana geliyor. Bu, iki kaynağın öncelik sırasını gösteriyor: önce
açıklama okunuyor, o yoksa varsayılana dönülüyor; kaynakta yazılı bir kural varsayılanın
yönünden bağımsız olarak kazanıyor. Bu öncelik sırası da kaynakta değil izinVer
yönteminin kendi gövdesinde duruyor — Bekci’yi yazan taraf hangi kaynağın önce
sorulacağını baştan kararlaştırmış.
Elle Yazılan Yetki Denetimi
Üçüncü kaynak, önceki ikisinden ayrı bir sınıfta duruyor: ElleYetkili, filtre zincirine hiç
girmeyen, @UcNokta ya da @Yetki taşımayan sıradan bir yöntem. Kararı burada nesne kabı
değil, yöntemin kendi gövdesindeki bir denetim ifadesi veriyor.
// ElleYetkili.java — yetki karari nesne kabi ya da aciklama uzerinden degil, dogrudan kaynaktaki bir denetim ifadesinden geliyor (yardimci kaynak, main yok)
class ElleYetkili {
String hassasIslem(String cagiranRolu) {
if (!"yonetici".equals(cagiranRolu)) throw new SecurityException("yetkisiz erisim");
return "hassas-veri";
}
String dene(String cagiranRolu) {
try { return hassasIslem(cagiranRolu); }
catch (SecurityException e) { return e.getClass().getSimpleName(); }
}
}
// ElleKontrol.java — ayni iki rolle cagrildiginda, kararin kaynaktaki bir kosuldan geldigini gosteriyor
public class ElleKontrol {
public static void main(String[] args) {
ElleYetkili elle = new ElleYetkili();
Bekci.gozle("hassasIslem, cagiran yonetici", "cagri", elle.dene("yonetici"));
Bekci.gozle("hassasIslem, cagiran calisan", "cagri", elle.dene("calisan"));
}
}
hassasIslem, cagiran yonetici cagri : hassas-veri hassasIslem, cagiran calisan cagri : SecurityException
Sonuç FiltreKarari dersindekiyle aynı görünüyor — yönetici geçiyor, çalışan reddediliyor —
ama kaynağı farklı. GT5. Elle yazılmış bir denetim ifadesi kararı doğrudan kaynaktan
verir; ne bir açıklama ne nesne kabı araya girer, karar hassasIslem yönteminin ilk
satırında duran karşılaştırmadan gelir. Bu üçüncü yol, Bekci’nin hiç yönetmediği bir
sınıfta yaşıyor; ElleYetkili nesnesi Bekci’ye hiç kaydolmadı, dolayısıyla ne yetki
açıklaması ne varsayılan yön ona uygulanabilir — bir önceki konunun kapanış dersindeki
gözlemle aynı sınır burada da geçerli: kabın dışında kalan nesne, kabın sunduğu hiçbir
düzeneği almaz.
Üç dersin de gösterdiği üç ayrı kaynak burada yan yana duruyor:
| Kaynak | Kararı veren yer | Bu derste örneği |
|---|---|---|
| açıklama | @Yetki işareti, Bekci tarafından okunur |
panel, surum |
| varsayılan | Bekci kurulurken verilen varsayilanAcikMi |
durum |
| çağrı | yöntemin kendi gövdesindeki denetim ifadesi | hassasIslem |
Aynı “yönetici mi değil mi” sorusu üç ayrı yerden üç ayrı biçimde yanıtlanıyor, ve kaynağı
yalnızca okuyan biri — açıklamaları görse bile — hangi yanıtın hangi davranışı ürettiğini
bilmeden yola çıkıyor. durum özelinde bu bilgisizlik daha da keskin: orada okunacak hiçbir
işaret yok, karar tümüyle kabın kuruluş anına ait. Bu üç kaynağın hiçbiri “doğru” ya da
“yanlış” değil; her biri farklı bir yerde tutuluyor ve kaynağı okuyan kişinin hangi yerlere
bakması gerektiğini önceden bilmesi gerekiyor — kurs boyunca kurulan ölçüm ekseni tam olarak
bu bilme ihtiyacını sayılabilir kılmak için var.
Üç kaynağın ayrımı bir de büyüklükle ilgili: açıklama ve varsayılan bütün bir sınıfı ya da
bütün bir kabı tek bir kararla kapsıyor, elle yazılan denetim ise yalnız yazıldığı yöntemi.
hassasIslem gibi on ayrı yöntem olsaydı, aynı karşılaştırma on kez tekrarlanırdı; işaret
tabanlı yaklaşım bu tekrarı ortadan kaldırıyor ama karşılığında kararı kaynaktan kaba
taşıyor. Bu ders çağıranın kimliğini zaten çözülmüş bir rol dizesi olarak aldı — "yonetici",
"calisan", "misafir" — ve bu dizenin nereden geldiğini hiç sormadı.
Özet
- Filtre zinciri, yetki açıklaması olan bir uç noktada kararı işaretten okur ve çağıranın rolüyle karşılaştırır; bu davranışın kaynağı açıklamadır.
- Yetki açıklaması olmayan bir uç noktada karar kabın kendi varsayılanından gelir; kaynak metni bu davranışı hiç göstermez.
- Aynı kaynak, yeniden derlenmeden, yalnız kabın kurulması sırasında verilen bir parametreyle iki farklı güvenlik duruşu üretebilir — sınırlayıcı ölçüm budur.
- Açıklama varsa varsayılan hiç devreye girmez; öncelik sırası önce açıklama, sonra varsayılandır.
- Elle yazılmış bir denetim ifadesi kararı doğrudan kaynaktan verir; bu yöntem kaba hiç kaydolmadığından ne açıklama ne varsayılan yön ona uygulanır.
Sonraki Adım
Bu ders çağıranın rolünü hazır bir dize olarak aldı — kim olduğu zaten bilinen bir girdiydi. Gerçek bir çağıran bu bilgiyi bir yerden taşımak zorunda, ve bu kurs sunucu tarafında bir oturum tutmuyor. Sıradaki ders bu rolün ve kimliğin sunucuda saklanan bir durumdan değil, çağıranın her istekte yanında getirdiği bir belirteçten nasıl geldiğini modelliyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.