Siber Güvenlik
Hibernate Envers ile Veri Denetimi
Günümüzde verinin çok önemli olması nedeniyle, uygulamalardaki veri değişikliklerini takip etmek oldukça tercih edilen bir gereksinim haline geldi. Bu amaca ulaşmak için çeşitli seçenekler bulunuyor ve “Hibernate Envers” da bu çözümlerden biridir.
Hibernate Envers nedir?
Hibernate Envers, entity’lerin versiyonlanmasını sağlayan bir projedir. Peki Hibernate Envers ne işe yarar? Hibernate Envers ile, “Entity”lerinizdeki tüm değişiklikleri kaydetmek ve denetlemek kolaydır. Bir varlığın tüm değişiklikleri ile geçmişi, denetim tablolarında bulunabilir. Ayrıca "kimin değişiklik yaptığı" gibi bazı ekstra bilgiler de denetim tablolarına eklenebilir.
Temel Kullanım
Projenizde Hibernate Envers kullanmak için, aşağıdaki bağımlılığı “pom.xml” dosyanıza eklemeniz yeterli olur:
Bir sonraki adım, denetlemek istediğiniz Entity class’ınıza @Audited anotasyonunu eklemektir. Bu anotasyon ile Hibernate-Envers, ekleme, güncelleme ve silme işlemlerinden kaynaklanan tüm değişiklikleri denetleyecektir. @Audited anotasyonu hem Entity için (tüm tablo için) hem de Kolonlar için (belirli bir alan için) eklenebilir.
@AuditTable, veri tabanındaki denetim tablosunun adını tanımlayan isteğe bağlı bir anotasyondur. Varsayılan durumda, Hibernate-Envers “tabloAdi_AUD” adıyla denetim tablosu oluşturur. Denetim tabloları için ek (tüm denetim tabloları) ve diğer yapılandırmalar uygulama özellikleri (properties) dosyasında değiştirilebilir.
Denetlenen bir varlığın bir alanı denetlemesi istenmediği durumda, @NotAudited anotasyonu kullanılmalıdır. Örnek olarak, şifre değişiklikleri denetim için önemli değildir. @NotAudited anotasyonu ile “password” alanındaki değişiklikler Hibernate-Envers tarafından yok sayılacaktır.
Not; “User” entity için " issues" ilişkisi de @NotAudited olarak işaretlenmiştir. Eğer bu ilişkinin denetlenmesi isteniyorsa, “Issue” entity classı da @Audited anotasyonu ile denetlenmelidir.
Şimdi, veri tabanında denetim tablolarını oluşturabiliriz. Hibernate yapılandırmasında "ddl-auto: update" kullanıyorsanız, tablolar otomatik olarak oluşturulacaktır.
Tabloları manuel olarak oluşturmak istiyorsanız, ilk oluşturulması gereken tablo "REVINFO" olmalıdır. Bu tablo, denetlenen tablolar için “primary key” olarak "rev" kolonunu tutar ve "revtstmp" kolonunu da değişiklik zamanını olarak belirtir.
Tüm varlıklarınız için denetim tabloları (eğer @Audited ise, örneğin "USERS_AUDIT") oluşturulmalıdır. “entity_audit” tablolarını oluştururken, denetlenen alanların farkında olmalı ve @NotAudited alanlarını eklememelisiniz. "rev" ve "revtype" alanları denetlenen tablolara eklenmelidir. Denetim tablosundaki "revtype", değişiklik tipi için kullanılır (0-> ekleme, 1->güncelleme, 2->silme).
Not; "rev" kolonunun artırılması için veri tabanında "sequence" (sıra) da oluşturulmalıdır.
Bu konfigürasyonlar ile Hibernate-Envers çalışmaya hazırdır. Başarılı bir yapılandırmadan sonra, veri tabanında aşağıdaki tabloları göreceksiniz.
Yukarıda görüldüğü gibi, USERS_AUDIT tablosunda tüm denetlenen alanlar (“password” hariç), ayrıca "rev" ve "revtype" alanları bulunmaktadır. Bundan böyle, “Users” objelerinde (USERS tablosundaki) tüm değişiklikler bu tabloda tutulacaktır.
Not; “Users” objesini değiştirmek için “native query” kullanırsanız, bu Hibernate-Envers tarafından yakalanamaz, bu nedenle bu tür değişiklikler denetlenemez.
Yeni Alanlarla Gelişmiş Kullanım
İlk kısım, Hibernate-Envers'ın temel kullanımıdır, bu bölümde yeni özellikler eklenecektir. Revizyon tablosu, denetlenen varlıkta herhangi bir değişiklik yapan kullanıcının "kullanıcı adı" veya "IP adresi" gibi yeni bilgilerle genişletilebilir.
Yeni alanlar eklemek için, “DefaultRevisionEntity”i implement eden özel bir “RevisionEntity” sınıfı yazılmalıdır. Yeni sınıf, denetlenen varlığı değiştiren "modifierUser" (değiştirici kullanıcı) gibi değişiklikler hakkında tutmak istediğiniz alanları içermelidir.
Not; MyRevisionEntity veri tabanında farklı bir tablo olarak oluşturulacaktır. Eğer MyRevisionEntity'den önce "revinfo" ve "audit" tabloları varsa, bu hataya neden olabilir. Bunu önlemek için, revinfo ve audit tablolarını silmeniz veya MyRevisionEntity tablo adını "revinfo" olarak değiştirmeniz gerekebilir.
MyRevisionEntity sınıfında görüldüğü gibi, @RevisionEntity anotasyonu özel bir RevisionListener sınıfı gerektirir. Bu, RevisionListener'ı implement etmeli ve "newVersion" metodunu override etmelidir. Bu method, RevisionEntity'de yeni eklenen alanı (modifierUser) yakalayacak şekilde yazılmalıdır.
"@Audited" anotasyonuna "withModifiedFlag = true" özelliği eklenebilir. Bu, hangi alanların değiştiğini bulmada yardımcı olur. Bu seçenek, denetim kayıtları için sorgular yazdığımızda daha net hale gelecektir.
Değişikliklerden sonra, son tablolar aşağıdaki gibi olmalıdır. Görüldüğü gibi, "revinfo" yerine "MY_REVISION_ENTITY" tablosu var ve "MODIFIER_USER" adında ekstra bir kolon var. Ayrıca, USERS_AUDIT tablosunda "_MOD" son eki olan yeni alanlar var. Bu, "withModifiedFlag = true" seçeneğiyle ilgilidir ve o kolonun değişip değişmediğini gösterir.
Denetim Kayıtlarında Sorgulama
Önceki bölümlerde, Hibernate-Envers varlıkla ilgili tüm değişiklikleri yakalar ve denetim günlükleri oluşturur demiştik. Bu denetim günlükleri arasında arama yapmak için AuditReader interface’i kullanılmalıdır. AuditReaderFactory aracılığıyla EntityManager veya Session'dan elde edilebilir.
İlk örnek sorgu, ".eq" filtresiyle belirli bir kullanıcının tüm geçmişini (revizyonlarını) almak için kullanılır. Filtrelenen özellik (bu örnekte = "id"), Entity sınıfındaki denetlenen alanlardan biri olmalıdır. "forRevisionsOfEntity(User.class, true, true)" ifadesindeki User.class, hangi varlığın kayıtlarının alınacağını tanımlar. Diğer parametreler "selectEntitiesOnly" ve "selectDeletedEntities"dir.
İlk örnekte "selectEntitiesOnly=true" seçilmiştir ve sonuç olarak, dönen sonuç listesi tipi User.class'tır. Peki, belirli bir kullanıcının revizyon bilgileriyle tüm geçmişini almak istesek ne olur? Bunun için "selectEntitiesOnly=false" kullanılmalıdır. Aşağıda görüldüğü gibi, dönen sonuç listesi tipi üç nesne dizisine değişmiştir:
- İlki değiştirilen varlıktır.
- İkincisi, "kim değiştirdi", "ne zaman değişti" ve "rev" bilgilerini tutan MyRevisionEntity'dir. Eğer özel RevisionEntity sınıfı kullanılmamışsa, dönüş tipi DefaultRevisionEntity olmalıdır.
- Üçüncüsü, işlem tipinin bir numaralandırması olan RevisionType'tır: ADD (ekleme), MOD (güncelleme), DEL (silme).
Yalnızca "email" alanı değişmiş kullanıcıların geçmişini almak için, sorguya ".hasChanged()" uygulanmalıdır. Bu sorguyu kullanabilmek için, @Audited anotasyonuna "withModifiedFlag=true" eklenmelidir. Daha spesifik sorgular yapmak için hem ".eq" hem de ".hasChanged()" aynı anda kullanılabilir.
Implement edilmiş RevisionEntity'e eklenen "modifierUser" alanını kullanma örneği de şu şekilde olacaktır. Belirli bir kullanıcı tarafından değiştirilmiş “Users” objesinin tüm geçmişini almak için aşağıdaki örnekte olduğu gibi revisionProperty kullanılmalıdır.
Not; Tüm sorgu seçenekleri ve örnekleri için, aşağıdaki linki verilen “Hibernate_User_Guide” sayfasına göz atabilirsiniz.
Hibernate User Guide