Alamat email yang valid merupakan gerbang untuk membangun komunikasi langsung, menghasilkan prospek, mendapatkan penjualan, undangan pribadi ke komunitas daring, dll. Jangan anggap remeh karena media sosial terus berubah. Melalui evolusi teknologi, email masih menjadi cara yang teruji dan benar untuk terhubung. Kita akan menjaga semuanya tetap sederhana dan tidak membuat kode dari awal karena Python memiliki modul yang ada untuk membantu Anda mempercepat pengodean.
Saya pernah memiliki pelanggan yang meminta saya membuat formulir pendaftaran email untuk mempromosikan produk mereka, tetapi tidak ada satu pun pelanggan yang mau membayar biaya bulanan untuk layanan pihak ketiga yang sudah jadi, jadi saya menghemat uang mereka dengan membuat formulir kontak khusus yang dapat mereka gunakan selamanya. Saya dapat membantu Anda melakukan hal yang sama, baik untuk keperluan startup, klien, pemasaran, atau yang terpenting, untuk mengurangi spam.
Tutorial ini ditujukan bagi siapa saja yang ingin belajar membuat kode dalam Python dan sangat berguna bagi pemula yang mungkin tidak mempertimbangkan fitur keamanan seperti memfilter masukan pengguna, memvalidasi alamat email, dan pendaftaran ganda melalui email. Dalam tutorial ini, kami membahas langkah 1-3:
Kami tidak perlu bergantung pada penggunaan layanan pihak ketiga seperti Auth0, Facebook, atau Google untuk mengakses aplikasi dan layanan Anda yang dapat mematikan aplikasi Anda kapan saja atau membagikan data Anda. Jaga data aplikasi Anda tetap milik Anda!
Pertama-tama, Anda harus memiliki pengalaman dalam Python karena kita akan menggunakan framework Flask dengan database MySQL . Ini akan lebih menyenangkan (mungkin) daripada menggunakan WordPress, CMS paling populer. Anda harus membayar beberapa plugin WordPress untuk mendapatkan kemampuan yang sama dengan ekstensi Flask gratis. Saya pernah membuat aplikasi di Wordpress (PHP) dan lebih suka Python Flask untuk aplikasi web meskipun Wordpress sangat mampu membuat aplikasi web.
Kami akan menggunakan modul Python yang ada yang menyederhanakan pembersihan masukan pengguna, pembuatan tautan verifikasi, dan komunikasi dengan basis data.
Setiap potongan kode akan dijelaskan dan akan menyertakan beberapa komentar dalam kode tersebut. Jika Anda belum membuat registrasi pengguna atau mengetahui cara kerja internalnya, saya akan menjelaskan detailnya untuk Anda, lalu Anda dapat melihat kode finalnya di bagian akhir (jangan lewatkan bagian ini).
Berikut adalah ringkasan fitur yang akan kami terapkan seperti yang dinyatakan di paragraf pertama:
Alamat email yang valid dapat diperiksa dengan mengurai string input dari pengguna menggunakan ekspresi reguler atau ekstensi Flask. Kami tidak akan mengizinkan peretasan teks acak atau injeksi SQL.
Metode double opt-in mengharuskan penerima untuk memberikan izin kepada Anda untuk mengirim email kepada mereka dengan menerima tautan validasi ke kotak masuk mereka. Hal ini terutama digunakan untuk mencegah orang lain menggunakan alamat email Anda. Hal ini juga mencegah pengguna uji yang hanya mendaftar dan meninggalkan akun mereka.
Pencegahan bot dapat dilakukan dengan kolom tersembunyi yang tidak diperlihatkan kepada pengguna tetapi umumnya diisi otomatis oleh bot yang mencari formulir pendaftaran yang rentan, tetapi tidak dapat diandalkan seperti "captcha" dari layanan pihak ketiga.
Mari kita mulai membuat kode. Buat direktori kerja:
mkdir signup cd signup
Buat lingkungan Python Anda menggunakan python3 -m venv signup
atau conda create -n double-opt-contact python3
. Saya lebih suka conda, dan jika Anda ingin mempelajari lebih lanjut, Anda dapat membaca artikel lingkungan Python saya.
Instal dependensi berikut:
pip flask flask-mail secure SQLAlchemy Flask-WTF Flask-SQLAlchemy mysql-connector-python bleach
Atau, Anda dapat mencantumkan dependensi yang sama dalam file requirements.txt
dan menjalankan pip install -r requirements.txt
Buat file app.py
dengan dependensi berikut yang disertakan:
from flask import Flask, render_template, request, url_for, redirect, flash from flask_mail import Mail, Message from datetime import datetime from flask_sqlalchemy import SQLAlchemy from sqlalchemy.sql import func from itsdangerous import URLSafeTimedSerializer, SignatureExpired import secrets import bleach
Inisialisasi objek aplikasi dengan lokasi folder templat default:
app = Flask(__name__, template_folder='templates')
Masukkan data konfigurasi server Anda sendiri menggunakan baris berikut:
secret = secrets.token_urlsafe(32) app.secret_key = secret app.config['SECRET_KEY'] = secret # auto-generated secret key # SQLAlchemy configurations app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://admin:user@localhost/tablename' # Email configurations app.config['MAIL_SERVER'] = 'smtp.example.com' app.config['MAIL_PORT'] = 465 #check your port app.config['MAIL_USERNAME'] = '[email protected]' app.config['MAIL_PASSWORD'] = 'your_password' app.config['MAIL_USE_TLS'] = True app.config['MAIL_USE_SSL'] = False db = SQLAlchemy(app) mail = Mail(app) sserialzer = URLSafeTimedSerializer(app.config['SECRET_KEY']) #set secret to the serliazer
Akhirnya, Anda harus memiliki info konfigurasi dalam file .env
.
Kita akan memerlukan basis data MySQL untuk menyimpan pengguna yang dapat dibuat secara manual atau dengan kode Python. Sebagai bagian dari proses pembelajaran, Anda dapat memasukkan kode berikut menggunakan baris perintah atau menggunakan metode with app.app_context() db_create_all()
Python.
Bidang yang divalidasi adalah untuk string token yang memperbolehkan teknik keikutsertaan ganda.
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(120) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, validated BOOLEAN DEFAULT FALSE );
Bagian berikutnya menggunakan struktur ORM SQLAlchemy untuk melakukan kueri pada basis data untuk Anda. Perhatikan bahwa nama kelas harus sesuai dengan nama tabel basis data Anda, jika tidak, Anda akan mendapatkan kesalahan. db.model
mewakili pengaturan tabel Anda yang mencakup nama kolom, jenisnya, panjangnya, kuncinya, dan nilai null-nya:
class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(120), unique=True, nullable=False) created_at = db.Column(db.DateTime, server_default=db.func.now()) validated = db.Column(db.Boolean, default=False)
Jika Anda belum membuat tabel database MySQL secara manual, Anda dapat melakukannya dengan kode Flask ini langsung setelah blok kode class User
:
# Create the database table with app.app_context(): db.create_all()
Sekarang, kita masukkan kode back-end yang terdiri dari 2 halaman/rute (indeks, pendaftaran), pesan email, dan konfirmasi. Halaman pendaftaran menyertakan metode GET/POST
yang memungkinkan formulir dikirimkan. Objek bleach
adalah ekstensi Python yang membersihkan input dari pengguna untuk memastikan keamanan dan mengurangi skrip berbahaya. Kemudian sserializer
menghasilkan token satu kali untuk mengirim tautan verifikasi melalui email.
@app.route('/') def index(): return '<h1>Index page</h1>' @app.route('/signup', methods=['GET', 'POST']) def signup(): if request.method == 'POST': email = bleach.clean(request.form.get('email')) # Insert user into the database new_user = User(email=email) try: db.session.add(new_user) db.session.commit() except Exception as e: print(f"Error occurred saving to db: {e}") # Send confirmation email token = sserialzer.dumps(email, salt='email-confirm') msg = Message('Confirm your Email', sender='[email protected]', recipients=[email]) link = url_for('confirm_email', token=token, _external=True) msg.body = f'Your link is {link}' try: mail.send(msg) except Exception as e: print(f"Error occurred sending message: {e}") flash("Error occurred sending message!") return render_template('signup.html') flash('A confirmation email has been sent to your email address.', 'success') return redirect(url_for('index')) return render_template('signup.html')
Sebelum menambahkan formulir pendaftaran HTML, mari selesaikan backend dengan menambahkan rute untuk memvalidasi fitur double opt-in. Rute ini menggunakan variabel s
yang kita buat sebelumnya yang menghasilkan token rahasia yang sensitif terhadap waktu. Lihat
Usia maksimal adalah detik sebelum tautan kedaluwarsa, jadi dalam kasus ini, pengguna memiliki waktu 20 menit untuk mengonfirmasi alamat email mereka.
@app.route('/confirm_email/<token>') def confirm_email(token): try: email = sserialzer.loads(token, salt='email-confirm', max_age=1200) # Token expires after 1 hour except SignatureExpired: return '<h1>The token is expired!</h1>' # Update field in database user = User.query.filter_by(email=email).first_or_404() user.validated = True db.session.commit() return '<h1>Email address confirmed!</h1>'
Sekarang, untuk pernyataan main yang ada di mana-mana yang memberi tahu Python untuk mengeksekusi skrip jika file dieksekusi secara langsung (bukan modul yang diimpor):
if __name__ == '__main__': app.run()
Sebelum kita melengkapi kode back-end ini, kita masih memerlukan HTML front-end untuk input pengguna. Kita akan melakukannya dengan templat bawaan Jinja Flask. Buat file bernama templates/signup.html
yang harus sesuai dengan nama rute yang Anda buat sebelumnya di app.py
. Secara default, Jinja menggunakan direktori /templates
untuk file html. Anda dapat mengubah pengaturan ini, tetapi untuk tutorial ini, kita akan menggunakan direktori /templates
dari aplikasi.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Email Sign Up</title> </head> <body> <h1>Sign Up</h1> <form action="{{ url_for('signup') }}" method="POST"> <input type="email" name="email" placeholder="Enter your email" required> <input type="submit" value="Sign Up"> </form> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} <ul> {% for category, message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} </body> </html>
Kode Anda seharusnya berfungsi mulai saat ini saat Anda menjalankan perintah flask dengan debugging diaktifkan. Ini akan memungkinkan Anda melihat kesalahan apa pun di baris perintah dan juga jendela browser:
flask --app app.py --debug run
Buka browser Anda ke domain yang ditampilkan di baris perintah (localhost) dan halaman indeks akan ditampilkan. Cobalah untuk mengirimkan formulir menggunakan alamat email yang valid untuk menerima tautan verifikasi. Setelah Anda mendapatkan tautan, tampilannya akan seperti http://localhost:5000/confirm_email/InRvbUByYXRldG91cmd1aWRlcy5jb20i.ZteEvQ.7o1_L0uM9Wl8uii7KhJdiWAH
, Anda dapat mengikutinya dan memvalidasi alamat email menggunakan rute validator yang ditampilkan di sini:
@app.route('/confirm_email/<token>') def confirm_email(token): try: email = sserializer.loads(token, salt='email-confirm', max_age=1200) # Token expires after 1 hour except SignatureExpired: return '<h1>Oops, the token expired!</h1>' # Update field in database user = Users.query.filter_by(email=email).first_or_404() user.validated = True try: db.session.commit() except Exception as e: print(f"Error occurred saving to db: {e}") return '<h1>Email address confirmed!</h1>'
Rute ini menerima string token yang sebelumnya dikirimkan kepada Anda dan memeriksanya untuk melihat apakah cocok dengan entri basis data terkait. Jika cocok, ia memperbarui bidang validated
menjadi True
, dan Anda dapat yakin bahwa formulir pendaftaran Anda tidak diabaikan.
Ini adalah langkah penting yang digunakan semua bisnis yang sukses dalam sistem registrasi mereka dan sekarang Anda juga mengalaminya. Tapi tunggu dulu, bagaimana jika kita mendapatkan serangan bot yang mengirimkan alamat email acak tanpa memvalidasinya? Maka Anda akan memiliki basis data yang kotor yang diisi dengan entri yang tidak berguna. Mari kita cegah itu!
Untuk mencegah serangan bot atau setidaknya mengurangi serangan tingkat lanjut, Anda dapat membangun solusi yang memakan waktu, termasuk pembatas IP yang memerlukan basis data dalam memori seperti Redis, atau Anda dapat menggunakan layanan pihak ketiga seperti captcha atau hCaptcha milik Google.
Dalam tutorial kami, kami akan menambahkan
Kami memerlukan persyaratan baru, jadi instal persyaratan tersebut:
pip install flask-hcaptcha requests
Permintaan diperlukan untuk mengirim alamat email ke hcaptcha untuk validasi. Dapatkan kuncinya, dan integrasikan berkas javascript hcaptcha dengan formulir pendaftaran HTML Anda. Tambahkan berkas ke bagian atas halaman HTML dan kunci situs Anda ke formulir Anda:
<head> ... <script src="https://hcaptcha.com/1/api.js" async defer></script> </head> <body> ... <form action="{{ url_for('signup') }}" method="POST"> <input type="email" name="email" placeholder="Enter your email" required> <input type="submit" value="Sign Up"> <div class="h-captcha" data-sitekey="b62gbcc-5cg2-41b2-cd5a-de95dd1eg61h" data-size="compact"></div> </form>
Kunci situs dalam kode ini adalah contoh; Anda akan memerlukan kunci situs Anda sendiri dari paket gratis. Kunci situs ini memvalidasi formulir Anda dan memeriksa pengunjung situs dengan daftar lengkap bot spam yang dikenal dengan hcaptcha.
Berikutnya, ubah file app.py
Anda untuk menyertakan kunci rahasia hcaptcha (bukan kunci situs) dalam objek app.config, dan posting respons ke hcaptcha sebelum menyimpannya ke basis data Anda sendiri.
app.config['HCAPTCHA_SECRET_KEY'] = 'your-secret-hcaptcha-key' ... @app.route("/signup", methods=['GET', 'POST']) def signup(): if request.method == 'POST': email = bleach.clean(request.form.get('email')) hcaptcha_response = request.form.get('h-captcha-response') # Verify hCaptcha response payload = { 'secret': app.config['HCAPTCHA_SECRET_KEY'], 'response': hcaptcha_response } try: response = requests.post('https://hcaptcha.com/siteverify', data=payload, timeout=10) result = response.json() except requests.exceptions.RequestException as e: print(f"Request failed: {e}") if not result.get('success'): flash('CAPTCHA validation failed, please try again.', 'danger') ... # Insert user into the database new_user = Users(email=email)
Setelah ini selesai, ikon hcaptcha akan muncul di formulir pendaftaran Anda, dan ikon tersebut harus diaktifkan untuk mencegah spam. Sekarang, Anda memiliki formulir yang lebih kuat untuk aplikasi baru Anda.
Jika Anda menemukan kesalahan atau kesalahan ketik pada kode, Anda dapat memeriksa kode yang sudah lengkap di
Berikan komentar jika Anda ingin lebih lanjut.