SignalR İle Authenticator Yapımı

Merhaba arkadaşlar, bu yazımızda SignalR ile authenticator yaparak mobil uygulama üzerinden web sitemize nasıl giriş yaparız ona değinmek istiyorum.

İnternet sitelerindeki hesap kaydı, giriş vs. gbi işlemlerde bildiğiniz üzere SMS ile kod gönderilir ve bu kod doğrulama alanında girilir ve kod doğru ise işlem tamamlanır. Bu işlemin alternatifi olarak özellikle büyük şirketlerin mobil authenticator uygulamaları vardır. Çalışma mantığı uygulamada üretilen kodu doğrudan web sitesinde ilgili kutucuğa girmektir. Biz de bunu biraz değiştirerek bir authenticator yapıyor olacağız.

Senaryo

Web sitesinde üye numarası kutucuğunu doldurarak gönderilen SMS’ i mobil uygulamamızda ilgili kutucuğa girerek web sitesindeki oturumumuzu başlatmak.

NOT: Yazıyı çok uzatmamak amacıyla önemli noktaları belirterek bir anlatım yapacağım. Projeyi yazının sonuna ekleyeceğim Github linkinden indirip inceleyebilirsiniz.

1.Nugettan projemize SignalR kütüphanesini ekliyoruz.

2.Dosya ve klasörleri oluşturma ve yapılandırma işlemi

Projemizin kök dizininde Startup classı oluşturuyoruz.

[assembly:OwinStartupAttribute(typeof(SignalRLogin.Startup))]
namespace SignalRLogin
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

Hubs klasörü oluşturup klasörün içerisinde bir adet LoginOperationHub adında bir class oluşturuyoruz ve SignalR kütüphanesini kurunca gelen Hub classından inheritance(miras) alıyoruz

ConnectionIdGetir metodu istemcinin bağlantı idsini almamıza yarıyor. Yani bir istemiciyi diğer istemciden bu sayede ayırt edebiliyoruz.Bu metodu kişi özel işlem yapacağımız zaman, sıklıkla kullanıyor olacağız.

public class LoginOperationHub : Hub
{
        private string ConnectionIdGetir()
        {
            return Context.ConnectionId;
        }
}

Models adında bir klasör oluşturarak projemizde kullanacağımız Kullanici ve LoginRepository adında Kullanici tablomuzdaki değişiklikleri dinlememize yardımcı olacak metodları yazacağımız classlarımızı oluşturuyoruz.

public class Kullanici
{
    public string UyeNo { get; set; }
    public string ConnectionId { get; set; }
    public string Kod { get; set; }
    public bool Durum { get; set; }

    public enum KullaniciDurum
    {
        Dogrulanmadi = 1,
        Dogrulandi = 2,
        HataliKod = 3
    }
}

    public class LoginRepository
    {
    }

3.Giriş ekranını düzenleme

<script src="~/Scripts/jquery.signalR-2.4.1.js"></script>
<script src="~/signalr/hubs"></script>
<br />
<br />
<br />
<div class="row">
    <div class="col-md-12">
        <div id="divGiris">
            <div class="row">
                <div class="col-md-12">
                    <input type="text" placeholder="Üye numarası" class="form-control" id="txtUyeNo" />
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <br />
                    <input type="button" class="btn btn-primary btn-block" value="Giriş Yap" id="btnGirisYap" />
                </div>
            </div>
        </div>

    </div>
</div>

<div class="row">
    <div class="col-md-12">
        <div id="divOnayla" style="display:none">
            <h3>Mobil uygulama üzerinden giriş onayı yapmanız gerekmektedir.</h3>
        </div>
    </div>
</div>
<div class="row">
    <div class="col-md-12">
        <div id="divBasariliGiris" style="display:none">
            <div class="alert alert-success" role="alert"><h3>Tebrikler Giriş Yaptınız.</h3></div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col-md-12">
        <div id="divHataliKod" style="display:none">
            <div class="alert alert-danger" role="alert"><h3>Hatalı Kod Girdiniz</h3></div>
        </div>
    </div>
</div>

4.Kullanıcı giriş işleminin tabloya kaydı ve Sms gönderme metodu

bildirim adında global bi değişken oluşturup viewimizde bu değişkene loginOperaitonHub’ ını gösteriyoruz.

var bildirim;
$(function () {
    bildirim = $.connection.loginOperationHub;
})
$("#btnGirisYap").click(function () {
    var txtUyeNo = $("#txtUyeNo").val();
    bildirim.server.girisKayit(txtUyeNo);
    $("#divGiris").css("display", "none");
    $("#divOnayla").css("display", "block");
    var paramData = JSON.stringify({ "kullaniciId": txtUyeNo });
    kullaniciDegisimKontrol(paramData)
})

girisKayit metodunu LoginOperationHub classında oluşturuyoruz

    public void GirisKayit(string kullaniciId)
    {
        LoginRepository loginRepository = new LoginRepository();
        loginRepository.GirisYap(kullaniciId, ConnectionIdGetir());
    }

LoginRepository classımıza aşağıdaki metodları ekliyoruz.

GirisDurum ve DependencyOnChange metodları sayesinde tablomuzdaki değişikliği dinliyoruz

GirisYap metodu ile kullanıcının tabloya kaydını yapıyoruz.

Not: SMS gönderme işlemide bu aşamada eklenebilir. Herhangi bir SMS firması APISI kullanımı bu aşamada paylaşmıyorum.

        readonly string cnn = ConfigurationManager.ConnectionStrings["loginConnection"].ConnectionString;

        string genelKullaniciId;
        public int GirisDurum(string kullaniciId)
        {
            genelKullaniciId = kullaniciId;
            using (SqlConnection con = new SqlConnection(cnn))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand(@"SELECT [Durum] FROM [dbo].[Kullanici] where UyeNo=@UyeNo", con))
                {
                    cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                    cmd.Notification = null;
                    SqlDependency dependency = new SqlDependency(cmd);
                    dependency.OnChange += new OnChangeEventHandler(DependencyOnChange);
                    if (con.State == ConnectionState.Closed)
                    {
                        con.Open();
                    }
                    return Convert.ToInt32(cmd.ExecuteScalar());
                }
            }
        }

        private void DependencyOnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                LoginOperationHub.SayfaDurumDegistir(genelKullaniciId);
            }
        }

        public bool GirisYap(string kullaniciId, string connectionId)
        {
            Random r = new Random();
            using (SqlConnection con = new SqlConnection(cnn))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand("insert into Kullanici(UyeNo,ConnectionId,Kod,Durum) values(@UyeNo,@ConnectionId,@Kod,@Durum)", con))
                {
                    cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                    cmd.Parameters.AddWithValue("@ConnectionId", connectionId);
                    cmd.Parameters.AddWithValue("@Kod", r.Next(1000, 10000).ToString());
                    cmd.Parameters.AddWithValue("@Durum", Kullanici.KullaniciDurum.Dogrulanmadi);
                    cmd.ExecuteNonQuery();
                }
            }
            return true;
        }

Kayıt işlemi yapılınca kullanıcıya bir SMS gidecektir. Kullanıcı gelen SMS’i mobil uygulamasında ilgili yere yazarak doğrulama işlemini yapacaktır.

5.Mobil uygulama giriş kodları HomeController

    [HttpPost]
    public void DurumGuncelle(string kullaniciId, string kod)
    {
        if (KodKontrol(kullaniciId, kod))
        {
            using (SqlConnection con = new SqlConnection(cnn))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand("update Kullanici set Durum=2 where UyeNo=@UyeNo", con))
                {
                    cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                    cmd.ExecuteNonQuery();
                }
            }
        }
        else
        {
            using (SqlConnection con = new SqlConnection(cnn))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand("update Kullanici set Durum=3 where UyeNo=@UyeNo", con))
                {
                    cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                    cmd.ExecuteNonQuery();
                }
            }
        }

    }


    public bool KodKontrol(string kullaniciId, string kod)
    {
        int durum = 0;
        using (SqlConnection con = new SqlConnection(cnn))
        {
            con.Open();
            using (SqlCommand cmd = new SqlCommand("if(exists(select * from Kullanici where UyeNo=@UyeNo and Kod=@Kod)) begin select 1 end else begin select 0 end", con))
            {
                cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                cmd.Parameters.AddWithValue("@Kod", kod);
                durum = Convert.ToInt32(cmd.ExecuteScalar());
                if (durum == 1)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }

Uygulamadan gelen bilgileri DurumGuncelle metoduna gönderiyoruz.

DurumGuncelle metodu uygulamadan gelen koda göre tablodaki durum alanını güncelleme işlemini yapıyor.

LoginOperationHub classımıza giderek aşağıdaki metodları ekliyoruz.

        public static void SayfaDurumDegistir(string kullaniciId)
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<LoginOperationHub>();
            List<string> kullaniciIds = new List<string>();
            kullaniciIds.Add(ConnectionIdGetirFromKullaniciId(kullaniciId));
            context.Clients.Clients(kullaniciIds).updateDurum();
        }
        private static string ConnectionIdGetirFromKullaniciId(string kullaniciId)
        {
            string cnn = ConfigurationManager.ConnectionStrings["loginConnection"].ConnectionString;

            using (SqlConnection con = new SqlConnection(cnn))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand("select ConnectionId from Kullanici where UyeNo=@UyeNo", con))
                {
                    cmd.Parameters.AddWithValue("@UyeNo", kullaniciId);
                    return cmd.ExecuteScalar().ToString();
                }
            }
        }

4.Maddede belirtilen DependencyOnChange metodundaki LoginOperationHub.SayfaDurumDegistir(genelKullaniciId); satır ile tablomuzdaki değişiklik hubdaki SayfaDurumDegistir metoduna bildiriliyor. Burdaki değişiklikte SayfaDurumDegistir metodunun altındaki context.Clients.Clients(kullaniciIds).updateDurum(); satırı ile istemci tarafına iletiliyor.

Ardından viewde aşağıdaki kullaniciDegisimKontrol metodunu ekliyoruz.

function kullaniciDegisimKontrol(paramData) {
    $.ajax({
        url: "/Home/KullaniciDurum",
        type: "POST",
        data: paramData,
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        success: function (gelenData) {
            if (gelenData == 2) {
                $("#divGiris").css("display", "none");
                $("#divOnayla").css("display", "none");
                $("#divBasariliGiris").css("display", "block");
                $("#divHataliKod").css("display", "none");
            }
            else if (gelenData == 3) {
                $("#divGiris").css("display", "none");
                $("#divOnayla").css("display", "none");
                $("#divBasariliGiris").css("display", "none");
                $("#divHataliKod").css("display", "block");
            }
        },
        error: function (x) {
            alert(x);
        }
    })
}

Daha önce eklediğimiz bildirim = $.connection.loginOperationHub; satırının altına aşağıdaki eklemleri yapıyoruz.

    bildirim.client.updateDurum = function () {
        var txtUyeNo = $("#txtUyeNo").val();
        var paramData = JSON.stringify({ "kullaniciId": txtUyeNo });
        kullaniciDegisimKontrol(paramData)
    }

Dikkat edecek olursanız bildirim.client.updateDurum hubımızdaki değişikliği istemci tarafına iletmemize yardımcı oluyor. Tablomuzda değişiklik oldukça hubdaki SayfaDurumDegistir metodu tetiklenecek. Bu metodda değişikliği updateDurum vasıtasıyla istemciye iletiyor olacak.

Son olarak tablomuzdaki değişiklik oldukça updateDurum function’ın içerisindeki kod blogu çalışacak. Buradaki kullaniciDegisimKontrol function’ ı ile de üye numarası parametre gönderilerek function içerisindeki kod doğruluğuna göre göster gizle işlemi yapılıyor.

Github Linki

https://github.com/MehmetAkifVurucu/SignalR-Mobil-Uygulama-Ile-Web-Giris-Ekrani

Arkadaşlar uzun bir yazı oldu. Elimden geldiğince açıklayıcı olmaya çalıştım. Gözden kaçan vs. bir yer olması durumunda yorum yazarak belirtirseniz düzeltmeye yardımcı olmaya çalışırım.

Bir sonraki yazıda görüşmek üzere

Related Post

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir