D ile günlük

Form kullanımı, yeni kullanıcılar eklemek

Bu makalede oluşturduğumuz günlüğe nasıl yeni kullanıcılar ekleyebileceğimizi öğreneceğiz.

Form kullanımı, yeni kullanıcılar eklemek

Öncelikle bu makalede sizi biraz yokuşa süreceğim. Ama eğer anlatılanları uygular ve anlarsanız, bu bilgilerle kendi sunucu uygulamanızı geliştirmek için önemli bir adım atmış olacaksınız.

Eğer hazırsanız koltuklarınıza yaslanın ve derse başlayalım.

Önceki derste sayfa desenlerinden bahsetmiştik.

İlk önce yeni kullanıcı kaydı ile başlayalım.

Bu derste de ilkönce views klasöründe kullanici isimli bir dizin oluşturuyoruz. Bu dizinde de olustur.dt isimli bir sayfa deseni oluşturuyoruz.

extends desenler/ana

block icerik
  .kullanici-olustur
    h1.icerik-altbaslik Yeni bir kullanıcı oluşturun
    section.kullanici-olustur
      form.pure-form.pure-form-aligned(method='post', action='/kullanıcı/oluştur')
        fieldset
          .pure-control-group
            label(for='isim') İsim
            |
            input#isim(name='isim', type='text')
          |
          .pure-control-group
            label(for='eposta') Elektronik posta
            |
            input#eposta(name="eposta", type='email')
          |
          .pure-control-group
            label(for='kullaniciismi') Kullanıcı ismi
            |
            input#kullaniciismi(name="kullaniciismi", type='text')
          |
          .pure-control-group
            label(for='sifre') Şifre
            |
            input#sifre(name='sifre', type='password')
          |
          .pure-control-group
            label(for='sifretekrar') Şifreyi tekrar giriniz
            |
            input#sifretekrar(name='sifretekrar', type='password')
            |
          |
          .pure-controls
            label.pure-checkbox(for='aligned-cb')
              input#aligned-cb(type='checkbox')
              |  Kullanım şartlarını okudum ve kabul ediyorum
            |
            button.pure-button.pure-button-primary(type='submit') Gönder

Buradaki formları oluşturmak için kullandığımız tasarım deseni olan Pure.css adresinden yararlandım.

Html etiketlerinden kullandığımız sayfa desenine çevirmek için bu araçtan faydalanabilirsiniz.

Bu aşamada kaynak kütüğüne oluşturduğumuz sayfa desenini ekleyip dub komutunu çalıştırarak yeni oluşturduğumuz kullanıcı kayıt sayfasının nasıl göründüğüne bakabilirsiniz.

import vibe.vibe;

void main()
{
    auto yolAtayıcı = new URLRouter;
    yolAtayıcı.get("/", staticTemplate!"gunluk/index.dt");
    yolAtayıcı.get("/kayıtol", staticTemplate!"kullanici/olustur.dt");
    yolAtayıcı.get("*", serveStaticFiles("public"));
    auto ayarlar = new HTTPServerSettings;
    ayarlar.port = 8080;
    ayarlar.bindAddresses = ["::1", "127.0.0.1"];
    listenHTTP(ayarlar, yolAtayıcı);

    logInfo("Lütfen tarayıcınızla http://127.0.0.1:8080/ adresini açınız.");
    runApplication();
}

İsterseniz tarayıcınızla uygulamayı derleyip http://localhost:8080/kayıtol adresinden ya da uygulamanın çalışan sürümü üzerinden kullanıcı kayıt sayfasının son halini görebilirsiniz.

Ancak bu durumda uygulamamız henüz kayıt yapmıyor.

Tekrardan uygulamanın kaynak kütüğünü bir metin düzenleyici ile açıyor ve aşağıdaki gibi değiştiriyoruz.

import vibe.vibe;
import vibe.db.mongo.mongo;
import std.datetime.systime : SysTime, Clock;

struct Kullanıcı
{
    @name("_id") int no;
    string isim;
    string şifre;
    SysTime kayıtZamanı;
}

int sonrakiKaydaGeç(string isim)
{
    auto veritabanı = connectMongoDB("localhost").getDatabase("kiraz");
    auto sayaç = veritabanı["sayaç"];
    auto sonuç = sayaç.findAndModifyExt(["_id": isim], ["$inc": ["değer": 1]], ["new": true]);
    logInfo("Sonuç %s", sonuç);
    return sonuç["değer"].get!int;
}

void kullanıcıOluştur (HTTPServerRequest istek, HTTPServerResponse yanıt)
{
    import std.array:empty;

    string isim = istek.form.get("isim");
    string ePosta = istek.form.get("eposta");
    string şifre = istek.form.get("sifre");
    string şifreTekrar = istek.form.get("sifretekrar");

    logInfo("Alınan bilgiler İsim = %s Eposta = %s", isim, ePosta);

    if (isim.empty || ePosta.empty || şifre.empty || şifre != şifreTekrar)
        yanıt.redirect("/kayıtol");
    else
    {
        auto veritabanı = connectMongoDB("localhost").getDatabase("kiraz");
        auto kullanıcılar = veritabanı["kullanıcılar"];

        Kullanıcı yeniKullanıcı;
        yeniKullanıcı.no = sonrakiKaydaGeç("kullanıcıno");
        yeniKullanıcı.isim = isim;
        yeniKullanıcı.şifre = şifre;
        yeniKullanıcı.kayıtZamanı = Clock.currTime();

        kullanıcılar.insert(yeniKullanıcı);
        yanıt.redirect("/");
    }
}

void main()
{
    auto yolAtayıcı = new URLRouter;
    yolAtayıcı.get("/", staticTemplate!"gunluk/index.dt");
    yolAtayıcı.get("/kayıtol", staticTemplate!"kullanici/olustur.dt");
    yolAtayıcı.post("/kullanıcı/oluştur", &kullanıcıOluştur);
    yolAtayıcı.get("*", serveStaticFiles("public"));
    auto ayarlar = new HTTPServerSettings;
    ayarlar.port = 8080;
    ayarlar.bindAddresses = ["::1", "127.0.0.1"];
    listenHTTP(ayarlar, yolAtayıcı);

    logInfo("Lütfen tarayıcınızla http://127.0.0.1:8080/ adresini açınız.");
    runApplication();
}

Yeni uygulamamıza parça parça bakacak olursak, kullanıcıOluştur() işlevinde kullanıcının girdiği form bilgilerini istek.form.get yordamıyla alıyoruz.

Eğer şifre, elektronik posta ve isim alanlarından herhangi birisi boşsa, kullanıcıyı tekrar kayıtol sayfasına yönlendiriyoruz.

Daha önce nasıl MongoDB ile veri okuyup yazabileceğinizden bahsetmiştik. Burada farklı olan kısım SysTime ile kayıt zamanını tutuyoruz. Clock.currTime() işlevi ise o anki zamanı veriyor.

MongoDB ile sayaç oluşturmak

Uygulamamızda, bir tane MongoDB kayıt numarasına _id karşılık gelen no alanının sonrakiKaydaGeç() işlevinin döndürdüğü tam sayı değeri aldığını görebilirsiniz.

Aslında MongoDB kayıt numarası _id alanı için sizden bir değer oluşturmanızı istemez. Bunu yukarıdaki uygulamada yeniKullanıcı isimli yapıdan no alanını çıkarak da kendiniz deneyebilirsiniz.

Diğer taraftan kayıt numarası _id alanı için 1, 2, 3 .. şeklinde değerler çok okunaklı oluyor.

Bu yöntemi kullanabilmek için gene kiraz veritabanında sayaç isimli bir belge ağacı oluşturup, içine şu bilgileri girebiliriz.

{
    "_id" : "kullanıcıno",
    "değer" : 0
}

Sayaç isimli belge ağacını oluşturmak için MongoDB komut satırını çalıştırdıktan sonra,

$ mongo

aşağıdaki komutları girebiliriz.

> use kiraz

> db.sayaç.insert({_id: "kullanıcıno" , değer: NumberInt(0)})

sonrakiKaydaGeç() işlevine bakacak olursak, bu işleve "kullanıcıno" değerini geçiyoruz.

Bu işlev yeni oluşturduğumuz sayaç isimli belge ağacından kayıt kimliği (_id alanı) "kullanıcıno" olan belgeyi buluyor. ["$inc": ["değer": 1]] ifadesi ise değer alanını bir arttırıyor.

Hatırlarsanız MongoDB kayıtlarının BSON veri biçiminde sakladığından bahsetmiştik.

auto sonuç değişkeni BSON türünde bir değer döndürüyor. En sonda da bu değeri bir tamsayıya döndürüyoruz.

BSON veri biçemi

Bu yazdığımız örnekler uygulamamızın bir parçası değil. Ancak isterseniz kendiniz deneyebilirsiniz.

Hatırlayabileceğiniz üzere BSON veri biçemi JSON'un ikili olarak saklanması ile oluşuyordu.

Örneğin {"isim": "Ali", "yaş": 22, "kayıtlı": true} şeklinde bir BSON nesnesi oluşturalım.

// normal kurucu ile
Bson ali = Bson(["isim": Bson("Ali"), "yaş": Bson(2), "kayıtlı": Bson(true)]);

Eğer istersek boş bir BSON nesnesi kullanarak, parça parça da oluşturabiliriz.

Bson veli = Bson.emptyObject;
veli['isim'] = "Veli";
veli['yaş'] = 18;
veli["kayıtlı"] = true;

Ya da bir yapı oluşturup bunu BSON veri biçemine çevirebiliriz.

struct Kullanıcı
{
    string isim;
    int yaş;
    bool kayıtlı;
}

Bson ayşe = Kullanıcı("Ayşe", 19, true).serializeToBson();

Eğer isterseniz uygulamanın çalışan sürümünün kayıt bölümünü inceleyebilir. Deneme amaçlı kayıt yapabilirsiniz.

Şifre güvenliği

Kullanıcının şifre bilgilerini sunucu üzerinde düz metin olarak saklamak, güvenlik açığı oluşturabilir.

Bunun yerine şifre bilgilerini sunucu üzerinde saklamayacağız. Şifreyi sadece kullanıcı bilecek. Biz sadece SHA-256 algoritması ile oluşturduğumuz tekli anahtarları saklayacağız.

SHA-256 algoritması ile nasıl tekli anahtar oluşturulduğunu bu makaleyi okuyarak öğrenebilirsiniz.

Tekli anahtar kullanarak şifreleri sakladığımızda MongoDB kullanıcı kayıtlarının nasıl göründüğünü görebilirsiniz.

{
    "_id" : 1,
    "isim" : "Erdem",
    "şifre" : { "$binary" : "rGbqfX46Y3Zgc/x3vU7341nV2TvfPcLcOFLp90+YiOs=", "$type" : "00" },
    "çeşni" : "15nlapcQZDCuNjOHLbKiaoCfguzBYQTC",
    "kayıtZamanı" : ISODate("2021-01-26T15:27:48.958+03:00")
}

Eğer isteseydik tekli anahtarları onaltılık sayı biçiminde metinlere çevirip de saklayabilirdik. Ama burada hem öğrenme, hem de deneme amaçlı anahtarın kendisini ikili olarak sakladık.

Uygulamada yaptığımız değişikliklere bakalım.

import vibe.vibe;
import vibe.db.mongo.mongo;
import std.datetime.systime : SysTime, Clock;
import anahtar.teklianahtar;

struct Kullanıcı
{
    @name("_id") int no;
    string isim;
    ubyte[32] şifre;
    string çeşni;
    SysTime kayıtZamanı;
}

int sonrakiKaydaGeç(string isim)
{
    auto veritabanı = connectMongoDB("localhost").getDatabase("kiraz");
    auto sayaç = veritabanı["sayaç"];
    auto sonuç = sayaç.findAndModifyExt(["_id": isim], ["$inc": ["değer": 1]], ["new": true]);
    logInfo("Sonuç %s", sonuç);
    return sonuç["değer"].get!int;
}

void kullanıcıOluştur (HTTPServerRequest istek, HTTPServerResponse yanıt)
{
    import std.array:empty;

    string isim = istek.form.get("isim");
    string ePosta = istek.form.get("eposta");
    string şifre = istek.form.get("sifre");
    string şifreTekrar = istek.form.get("sifretekrar");

    logInfo("Alınan bilgiler İsim = %s Eposta = %s", isim, ePosta);

    if (isim.empty || ePosta.empty || şifre.empty || şifre != şifreTekrar)
        yanıt.redirect("/kayıtol");
    else
    {
        auto veritabanı = connectMongoDB("localhost").getDatabase("kiraz");
        auto kullanıcılar = veritabanı["kullanıcılar"];

        Kullanıcı yeniKullanıcı;
        yeniKullanıcı.no = sonrakiKaydaGeç("kullanıcıno");
        yeniKullanıcı.isim = isim;
        yeniKullanıcı.çeşni = çeşniÜret();
        auto anahtar = tekliAnahtarÜret(şifre,yeniKullanıcı.çeşni);
        yeniKullanıcı.şifre = anahtar;
        yeniKullanıcı.kayıtZamanı = Clock.currTime();
        assert(anahtarıDoğrula(anahtar, şifre, yeniKullanıcı.çeşni));
        kullanıcılar.insert(yeniKullanıcı);
        yanıt.redirect("/");
    }
}

void main()
{
    auto yolAtayıcı = new URLRouter;
    yolAtayıcı.get("/", staticTemplate!"gunluk/index.dt");
    yolAtayıcı.get("/kayıtol", staticTemplate!"kullanici/olustur.dt");
    yolAtayıcı.post("/kullanıcı/oluştur", &kullanıcıOluştur);
    yolAtayıcı.get("*", serveStaticFiles("public"));
    auto ayarlar = new HTTPServerSettings;
    ayarlar.port = 8080;
    ayarlar.bindAddresses = ["::1", "127.0.0.1"];
    listenHTTP(ayarlar, yolAtayıcı);

    logInfo("Lütfen tarayıcınızla http://127.0.0.1:8080/ adresini açınız.");
    runApplication();
}

Burada import anahtar.teklianahtar; ifadesi ile yukarıdaki makalede oluşturduğumuz teklianahtar.d isimli kütüğü uygulamamıza ekledik.

Kullanıcı isimli yapıda şifre alanını ubyte[32] olarak belirledik. Böylece anahtarın kendisini ikili olarak saklayabileceğiz. Ek olarak çeşni isimli bir alan ekledik.

Uygulamamızın kaynak bölümündeki değişikliklere bakacak olursak,

    yeniKullanıcı.çeşni = çeşniÜret();

kısmında rastgele bir çeşni üretiyoruz.

    auto anahtar = tekliAnahtarÜret(şifre,yeniKullanıcı.çeşni);
    yeniKullanıcı.şifre = anahtar;

Bu kısımda da SHA256 ile yeni bir tekli anahtar oluşturuyoruz. Kullanıcının şifre bilgilerini kaydetmediğimize dikkatinizi çekmek istiyorum.

    assert(anahtarıDoğrula(anahtar, şifre, yeniKullanıcı.çeşni));

Buradaki assert ifadesini, yazılımın bunu yapacağından eminim anlamında kullanıyoruz.

Yukarıdaki kullanımda sadece tekli anahtar oluşturan işlevin değil, oluşturulan tekli anahtarı doğrulayan işlevin de doğru çalıştığını teyit etmek için kullanıyoruz.

Aksi halde yazılımın bir aykırı durum oluşturup, veritabanına kayıt yapmaması gerekiyor.

Kullandığımız kaynak kütüğü ve tekli anahtar oluşturan kütüğü buradan indirebilirsiniz.

Kullanıcı kayıt sayfasında hala bazı geliştirmeler yapabiliriz.

Bunlardan bir tanesi kayıt alanlarından bir tanesi eksik ya da hatalı doldurulduğunda kullanıcıya geri bildirim göndermek olabilir.

Kullanıcı doğrulaması için ise, kullanıcıya bir elektronik posta göndermek düşünülebilir.

Yorumlar

yorum yaz

Yorum yaz

Henüz yorum yok.