10 Dakikada bir tıklanabilen buton
Merhaba, 1 Kullanıcı ve 1 İlan tablom var. Kullanıcılar ilanlarını ekleyip, yayına alabiliyor. İstek üzerine "Üste taşıma" butonu eklemeye karar verdim fakat butonu aktif olarak bırakmak istemiyorum.
Yaklaşık 2 saattir nasıl bir mantık kurabilirim diye düşünüyorum ama saatten mi yoksa bilgimin yetersiz olduğundan mı bilemiyorum ama kodu tam yazmaya başlarken "o iş öyle olmaz ki" diye beynim uyarıp duruyor.
Google'da çeşitli forumlarda yapmak istediğim konuyu aradım fakat herkes mantığı anlatıp, bırakıp gitmiş. çoğu mantıksız bir mantık anlatıp bir daha cevap bile vermemiş neredeyse.
Yapmak istediğim şey tam olarak şu;
İlanlar listesinde her kullanıcının kendi ilanında görebileceği "Yukarı Taşı" butonunun olması ve bu butona tıklandığı zaman işlem sayfasında tarihe +10 ekleyip, 10 dakika boyunca bu butona tekrar tıklanmamasını sağlamak.
Aslında gayet açık ve net okuduğum zaman kendime anlatabiliyorum ama iş kod yazmaya geldiği zaman tarihe nasıl + 10 ekleyip, o butonu 10 dakika sonra aktif veya pasif edeceğim kısmını kendime anlatamıyorum. Bu konu için bana yardım edebilir misiniz? şimdiden cevaplar için teşekkür ederim.
Soru hatalı mı? 👎
Eğer sorunun kurallara aykırı olduğunu düşünüyorsanız lütfen bize bildirin!
Cevaplar (8)
Merhaba. Etikete PHP girmişsiniz ama front-end tarafında bu sorunu çözebilirsiniz.
Bir butona tıklandığı anda tarihi alır, üzerine 10dk ekler ve bu değeri butona atribute olarak eklersiniz.
Butona her tıklandığında bu attribute'ü kontrol edersiniz ve henüz zamanı gelmemişse işlem yaptırmazsınız.
Örnek olarak (JQuery ile):
const isButtonFree = ($button) => {
const now = (new Date()).getTime();
const afterTenMinutesTime = (new Date(now + 10 * 60 * 1000)).getTime();
const buttonCode = Number($button.attr('data-code'));
if(isNaN(buttonCode) || buttonCode < now) {
$button.attr('data-code', afterTenMinutesTime);
return true;
}
return false;
};
$(document).on("click", "button.sendToUp", function() {
if(!isButtonFree($(this))) { alert("Son tıklamanın üzerinden 10 dk geçmemiş!"); return; }
// Butona tıklanınca normalde yapılan işlemler...
alert("Butona tıklanabildi");
});
Tarayıcıda konsolu kullanmayı bilenler bu işlemi aşamasınlar, diyorsanız ve butona her tıklama işlemi sunucuya bir istek atıyorsa yukarıda verdiğim kodun üstüne bir de PHP tarafında token oluşturmanız ve her butona basışta sunucu tarafında token kontrolü yapmanız gerekir.
Bu mantıkta da PHP tarafında request'e özel bir token üretecek ve response üzerinden bunu tarayıcıya göndereceksiniz.
Tarayıcı bu kodu alıp butona attribute olarak basacak.
Butona her basışta attribute üzerinden bu token değeri okunacak ve request'e eklenecek.
Bu token, içinde 10 dk sonrasının verisini şifreli şekilde tutan ve şifrenin anahtarını bilmeyenin şifreyi çözemeyeceği bir kod olmalı.
Bu konu, Tayfun ERBİLEN'in PHP ile Verileri Şifrelemek makalesinden yardım alınarak çözülebilir.
Şimdi diyelim ki siz butona basıldığında işlem yapıp yapmayacağınızı belirlemek için MoveUp.php sayfasına post isteği atıyorsunuz ve yanıta göre işlem yapıp yapmayacağınıza karar veriyorsunuz.
MoveUp.php
// Aynı kodları tekrar tekrar yazmamak için bir fonksiyon...
function sendResponse($val) {
header("Content-type: application/json; charset=utf-8");
echo json_encode($val);
exit();
}
// Token şifreleme algoritması ve şifre anahtarı verileri...
$tokenKey = 'prototurk.2022.xx1';
$tokenCipher = 'AES-128-ECB';
// POST parametrelerini alalım.
$token = isset($_POST["token"]) ? $_POST["token"] : null;
$id = isset($_POST["id"]) ? $_POST["id"] : null; // başka parametreler de göndermişsiniz diyelim...
// token veya id değeri yoksa hata döndürelim.
if(!$token) sendResponse(["error"=>"token required", "errorCode"=>1]);
if(!$id) sendResponse(["error"=>"id required", "errorCode"=>1]);
// token çözülememişse (değiştirilmiştir muhtemelen) hata dönelim.
$decodedToken = openssl_decrypt($token, $tokenCipher, $tokenKey);
if(!$decodedToken) sendResponse(["error"=>"token hatalı", "errorCode"=>2]);
// token içindeki değer bir timestamp değeri. Bu değeri şu anın timestamp'iyle karşılaştıralım.
// Eğer henüz token'daki tarih şu anki tarihten önce değilse henüz 10 dakikanın geçmediğine dair hata dönelim.
$tokenTime = (int)$decodedToken;
$now = strtotime("now");
if($now < $tokenTime) sendResponse(["error"=>"It hasn't been 10 minutes.", "errorCode"=>3);
// Buraya kadar her şey yolundaysa butonun 10 dakikası geçmiş demektir.
// Şu andan itibaren 10 dakika sonrasını bulup şifreleyelim.
$afterTenMinutes = strtotime("+10 minute");
$newToken = openssl_encrypt($afterTenMinutes, $tokenCipher, $tokenKey);
// Bundan sonra id gibi diğer parametreleri kullanarak gerekli işlemlerimizi yapıyoruz.
// Sonuç olarak döneceğimiz cevapta mutlaka yeni token değerini de gönderiyoruz ki butonun attribute'sine yeni token değeri yazılabilsin.
sendResponse["success"=>"It can move to up!", "token"=>$newToken];
Bu sayfa sizin butona basıldıktan sonra sunucuya istek attığınız ve sunucu taraflı işleri hallettiğiniz sayfa.
Bu sayfaya POST metoduyla "token" değerini göndermezseniz veya değiştirilmiş bir token gönderirseniz size hata verir.
Haliyle butonlar ilk oluştukları zamanda herhangi bir token değerine sahip olmadıkları için bu sayfaya atacağınız her istek hata döner.
Bu nedenle butonları oluştururken hemen token'larını da vermelisiniz.
Örneğin butonlarınızın bulunduğu front-end sayfanız şöyle olacak:
<?php
// Token şifreleme algoritması ve şifre anahtarı verileri...
$tokenKey = 'prototurk.2022.xx1';
$tokenCipher = 'AES-128-ECB';
$firstToken = openssl_encrypt(strtotime("+10 minute"), $tokenCipher, $tokenKey);
?>
<div class="moveableElement" data-id="123">
<div class="elementTitle">Örnek içerik 1</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
<div class="moveableElement" data-id="132">
<div class="elementTitle">Örnek içerik 2</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
<div class="moveableElement" data-id="231">
<div class="elementTitle">Örnek içerik 3</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
Bu örnekte sayfaya girildiği andan itibaren tüm butonlar, 10 dakika sonrasının şifrelenmiş halini token olarak almış olacaklar.
Yani biri sayfaya ilk kez girerse veya eskaza sayfayı yenilerse 10 dk boyunca yukarı taşıma yapamayacak.
Şimdi daha önce front-end taraflı kontrol sağladığımız kodda istek atma örneği yapalım.
Diyelim ki butona basınca üst elementin data-id
attribute'sini alıp veriyi PHP'ye ajax ile iletiyoruz...
const isButtonFree = ($button) => {
const now = (new Date()).getTime();
const afterTenMinutesTime = (new Date(now + 10 * 60 * 1000)).getTime();
const buttonCode = Number($button.attr('data-code'));
if(isNaN(buttonCode) || buttonCode < now) {
$button.attr('data-code', afterTenMinutesTime);
return true;
}
return false;
};
$(document).on("click", "button.sendToUp", function() {
const $thisButton = $(this);
if(!isButtonFree($thisButton)) { alert("Son tıklamanın üzerinden 10 dk geçmemiş!"); return; }
const itemId = $(this).closest(".moveableElement").attr("data-id");
const itemToken = $(this).attr("data-token");
$.ajax({
url:"MoveUp.php",
type:"POST",
data: { id:itemId, token:itemToken },
success: (res) => {
// Eğer sunucu token göndermişse butondaki data-token'ı hemen güncelleyelim.
if(res.token) $thisButton.attr('data-token', res.token);
if(res.error) {
if(res.errorCode===1) alert("eksik parametre");
else if(res.errorCode===2) alert("token hatalı! Sayfayı yenileyin");
else if(res.errorCode===3) alert("Henüz 10 dakika geçmemiş...");
return;
}
if(res.success) {
alert("Tamam. Yukarı Taşıma işlemi yapılabilir...");
return;
}
},
error: (err) => {
alert("Sunucuyla iletişimde problem...");
}
});
});
Muhakkak sizin yapmak istediğiniz şeyler biraz daha farklıdır. Ama bu cevaptan yola çıkarak fikir yürütebilirsiniz. Bundan daha detaylı cevap alabileceğinizi sanmıyorum. :)
Aslında ilk yazdığım kod bloğundaki işlemler çoğu kullanıcının sürekli Yukarı Taşı yapmasını önleyecektir.
PHP taraflı kontrollerimizle kötü niyetli kullanıcıların bir çoğunu engellemiş olduk.
Peki kötü niyetli bir kullanıcı bu token'la kurduğumuz güvenlik katmanını aşamaz mı? Yeterince tecrübeliyse kolaylıkla aşabilir. Mesela sayfa ilk yüklendiğinde butona verilen token'ı kopyalar. Sonra ilk Yukarı Taşıma işleminden sonra biz javascript'le yeni token'ı butona veririz. Evet bu token'ı silerse veya değiştirirse işlem yapamaz ama ilk kopyaladığı token halen PHP tarafından kabul edilen bir token olduğuna göre bu token'ı istediği butona yapıştırıp tekrar tekrar Yukarı Taşıma işlemi yapabilir. Peki bu kötü niyetli kullanıcı için ne yapabiliriz? Mesela şifreleme yaparken token'a sadece tarih verisini değil aynı zamanda data-id attribute'sindeki değeri de katarız. Böylece her butonun kendine özgü bir token'ı olur. MoveUp.php sayfası da hem tarihi hem de id değerini birlikte konrol eder. Peki böyle yaparak her butonun kendine özgü bir token'ı olmasını sağlamak bu kötü niyetli kullanıcıyı durdurabilir mi? Hayır. Sadece aynı kodu istediği her butonda kullanamamasını sağlamış oluruz. Ama her butonun token'ını yine de kopyala yapıştırla yeniden kullanabilir. Demek ki bir token'ın kullanıldıktan sonra tekrar kullanılamamasını sağlayacak bir algoritmamız olması lazım...
Bunlar daha derinlikli siber güvenlik konuları olduğundan bambaşka bir soru'nun konusu olabilir...
@h4ckdr0 hocam öncelikle yanıtınız için teşekkür ederim fakat konuyu çözüp, daha güvenli duruma getirdim hem @ebykdrms hocam sayesinde hem de araştırarak. nezaket gösterip böyle bi kaynak daha sunduğunuz için teşekkürler. işine yarayan olursa mutlaka kullanacaktır.
PHP Kodu:
<!--
1. PHP ile 10 dakikada bir tıklanabilen buton oluştur
2. kalan süreyi saat dakika saniye olarak bir div içerisinde göster
3. süre bittiğinde butonu tekrar tıklanabilir hale getir
-->
<?php
$lastTime = time(); // 1. butonun son tıklanma zamanını al
$lastTime = $lastTime + 600; // 1. butonun son tıklanma zamanına 10 dakika ekle
$lastTime = date("Y-m-d H:i:s", $lastTime); // 1. butonun son tıklanma zamanını tarih formatına çevir
$now = time(); // 2. şu anki zamanı al
$end = strtotime($lastTime); // 2. butonun son tıklanma zamanını tarih formatından çıkar
$diff = $end - $now; // 2. butonun son tıklanma zamanından şu anki zamanı çıkar
// $hours = floor($diff / 3600); // 2. kalan süreyi saat olarak al
$mins = floor($diff / 60 % 60); // 2. kalan süreyi dakika olarak al
$secs = floor($diff % 60); // 2. kalan süreyi saniye olarak al
// echo $hours . " saat " . $mins . " dakika " . $secs . " saniye";
// echo $mins . " dakika " . $secs . " saniye";
?>
HTML Kodu:
<div id="time"></div> <!-- 2. kalan süreyi saat dakika saniye olarak bir div içerisinde göster -->
<button id="btn">Tıkla</button> <!-- 1. butonu oluştur -->
<script>
let btn = document.getElementById("btn"); // 1. butonu seç
let time = document.getElementById("time"); // 2. kalan süreyi saat dakika saniye olarak bir div içerisinde göster
let mins = <?php echo $mins; ?>; // 2. dakika olarak al
let secs = <?php echo $secs; ?>; // 2. saniye olarak al
btn.addEventListener("click", () => { // 1. butona tıklandığında
btn.disabled = true; // 1. butonu tıklanamaz hale getir
let timer = setInterval(() => { // 2. süre bittiğinde butonu tekrar tıklanabilir hale getir
if (mins == 0 && secs == 0) {
btn.disabled = false; // 2. butonu tekrar tıklanabilir hale getir
clearInterval(timer); // 2. süre bittiğinde butonu tekrar tıklanabilir hale getir
} else {
if (secs == 0) { // 2. saniye 0 ise
mins--; // 2. dakikayı 1 azalt
secs = 59; // 2. saniyeyi 59 yap
} else {
secs--; // 2. saniyeyi 1 azalt
}
time.innerHTML = mins + " dakika " + secs + " saniye"; // 2. kalan süreyi saat dakika saniye olarak bir div içerisinde göster
}
}, 1000); // 2. süre bittiğinde butonu tekrar tıklanabilir hale getir
});
</script>
Umarım yardımı dokunur
<!--
1. JS ile 10 dakikada bir tıklanabilen buton oluşturuyoruz
2. kalan süreyi saat dakika saniye olarak bir div içerisinde gösteriyoruz
3. süre bittiğinde butonu tekrar tıklanabilir hale getiriyoruz
-->
<button id="btn">Tıkla</button>
<br><br>
<div id="time"></div>
<script>
let btn = document.getElementById("btn"); // butonu seçtik
let time = document.getElementById("time"); // div'i seçtik
let timeLeft = 600; // 10 dakika = 600 saniye
let timer = null; // timer değişkeni oluşturduk
btn.addEventListener("click", () => { // butona tıklandığında
if (timer == null) { // timer boşsa
btn.disabled = true; // butonu devre dışı bırak
timer = setInterval(() => { // timer değişkenine setInterval fonksiyonunu atadık
timeLeft--; // her saniyede 1 azalttık
// let hour = Math.floor(timeLeft / 3600); // saat
let minute = Math.floor((timeLeft % 3600) / 60); // dakika
let second = timeLeft % 60; // saniye
// time.innerHTML = `Kalan Süre: ${hour} : ${minute} : ${second}`;
time.innerHTML = `Kalan Süre: ${minute} : ${second}`; // dakika saniye olarak yazdırdık
if (timeLeft == 0) { // süre bittiğinde
clearInterval(timer); // timer'ı durdurduk
timer = null; // timer değişkenini boşalttık
btn.disabled = false; // butonu tekrar tıklanabilir hale getirdik
timeLeft = 600; // süreyi tekrar 600 saniye yaptık
}
}, 1000); // her saniyede bir çalıştır
}
});
</script>
Tüm cevaplar için çok teşekkür ederim. @ebykdrms genel olarak frontend üzerinden yapmak istemesem de bana ışık tuttunuz en azından mantık konusunda biraz daha aydınlandım.
Evet şifreleme konusunda ben de Tayfun hocamın paylaştığı makaleden yararlanıyorum uzun süredir hatta çoğu değişkeni gizli tutmaya çalışıyorum elimden geldiğince.
Tüm cevaplar için çok teşekkür ederim.
@goko: ne olmuşsa olmuş gülüm. ben değiştirmek istemem artık.
frontend tarafında yapılan tüm işlemler kullanıcının müdahalesine açıktır. Onun için güvenli ve kullanıcı müdahalesine izin vermeksizin bir şey yapmak istiyorsanız o zaman backend kısmında yapacaksınız. Eğer php kullanıyorsanız tıklama anında yapılan işlem zaman damgasını bir sessionda tutun, tekkar tıklansa bile istediğiniz süre dolmadıysa işlem yapmazsınız.