邮件验证码搞定
This commit is contained in:
@@ -24,6 +24,10 @@
|
|||||||
<input id="code" name="code" type="text" required />
|
<input id="code" name="code" type="text" required />
|
||||||
<label for="email">邮箱</label>
|
<label for="email">邮箱</label>
|
||||||
<input id="email" name="email" type="email" required />
|
<input id="email" name="email" type="email" required />
|
||||||
|
<button id="sendCodeBtn" type="button">发送验证码</button>
|
||||||
|
<div id="sendMsg" class="hint"></div>
|
||||||
|
<label for="email_code">邮箱验证码</label>
|
||||||
|
<input id="email_code" name="email_code" type="text" required />
|
||||||
<label for="username">用户名</label>
|
<label for="username">用户名</label>
|
||||||
<input id="username" name="username" type="text" required />
|
<input id="username" name="username" type="text" required />
|
||||||
<label for="password">密码</label>
|
<label for="password">密码</label>
|
||||||
@@ -43,20 +47,36 @@
|
|||||||
const code=(document.getElementById('code').value||'').trim();
|
const code=(document.getElementById('code').value||'').trim();
|
||||||
const email=(document.getElementById('email').value||'').trim();
|
const email=(document.getElementById('email').value||'').trim();
|
||||||
const username=(document.getElementById('username').value||'').trim();
|
const username=(document.getElementById('username').value||'').trim();
|
||||||
|
const email_code=(document.getElementById('email_code').value||'').trim();
|
||||||
const password=document.getElementById('password').value||'';
|
const password=document.getElementById('password').value||'';
|
||||||
const confirm=document.getElementById('confirm').value||'';
|
const confirm=document.getElementById('confirm').value||'';
|
||||||
if(!code||!email||!username||!password){err.textContent='请填写所有字段';return;}
|
if(!code||!email||!email_code||!username||!password){err.textContent='请填写所有字段';return;}
|
||||||
if(password!==confirm){err.textContent='两次密码不一致';return;}
|
if(password!==confirm){err.textContent='两次密码不一致';return;}
|
||||||
const btn=document.getElementById('regBtn'); btn.disabled=true;
|
const btn=document.getElementById('regBtn'); btn.disabled=true;
|
||||||
try{
|
try{
|
||||||
const csrftoken=getCookie('csrftoken');
|
const csrftoken=getCookie('csrftoken');
|
||||||
const resp=await fetch('/accounts/register/submit/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({code,email,username,password})});
|
const resp=await fetch('/accounts/register/submit/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({code,email,email_code,username,password})});
|
||||||
const data=await resp.json();
|
const data=await resp.json();
|
||||||
if(!resp.ok||!data.ok){throw new Error(data.message||'注册失败');}
|
if(!resp.ok||!data.ok){throw new Error(data.message||'注册失败');}
|
||||||
window.location.href=data.redirect_url;
|
window.location.href=data.redirect_url;
|
||||||
}catch(e){err.textContent=e.message||'发生错误';}
|
}catch(e){err.textContent=e.message||'发生错误';}
|
||||||
finally{btn.disabled=false;}
|
finally{btn.disabled=false;}
|
||||||
});
|
});
|
||||||
|
document.getElementById('sendCodeBtn').addEventListener('click',async()=>{
|
||||||
|
const email=(document.getElementById('email').value||'').trim();
|
||||||
|
const msg=document.getElementById('sendMsg');
|
||||||
|
msg.textContent='';
|
||||||
|
if(!email){msg.textContent='请输入邮箱';return;}
|
||||||
|
const btn=document.getElementById('sendCodeBtn'); btn.disabled=true;
|
||||||
|
try{
|
||||||
|
const csrftoken=getCookie('csrftoken');
|
||||||
|
const resp=await fetch('/accounts/email/send-code/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({email})});
|
||||||
|
const data=await resp.json();
|
||||||
|
if(!resp.ok||!data.ok){throw new Error(data.message||'发送失败');}
|
||||||
|
msg.textContent='验证码已发送,请查收邮件';
|
||||||
|
}catch(e){msg.textContent=e.message||'发送失败';}
|
||||||
|
finally{btn.disabled=false;}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -11,4 +11,5 @@ urlpatterns = [
|
|||||||
path("logout/", views.logout, name="logout"),
|
path("logout/", views.logout, name="logout"),
|
||||||
path("register/", views.register_page, name="register"),
|
path("register/", views.register_page, name="register"),
|
||||||
path("register/submit/", views.register_submit, name="register_submit"),
|
path("register/submit/", views.register_submit, name="register_submit"),
|
||||||
|
path("email/send-code/", views.send_email_code, name="send_email_code"),
|
||||||
]
|
]
|
||||||
@@ -4,6 +4,8 @@ import os
|
|||||||
import io
|
import io
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
import time
|
||||||
|
import smtplib
|
||||||
|
|
||||||
from django.http import JsonResponse, HttpResponseBadRequest
|
from django.http import JsonResponse, HttpResponseBadRequest
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
@@ -183,10 +185,22 @@ def register_submit(request):
|
|||||||
return HttpResponseBadRequest("Invalid JSON")
|
return HttpResponseBadRequest("Invalid JSON")
|
||||||
code = (payload.get("code") or "").strip()
|
code = (payload.get("code") or "").strip()
|
||||||
email = (payload.get("email") or "").strip()
|
email = (payload.get("email") or "").strip()
|
||||||
|
email_code = (payload.get("email_code") or "").strip()
|
||||||
username = (payload.get("username") or "").strip()
|
username = (payload.get("username") or "").strip()
|
||||||
password = (payload.get("password") or "")
|
password = (payload.get("password") or "")
|
||||||
if not code or not email or not username or not password:
|
if not code or not email or not email_code or not username or not password:
|
||||||
return HttpResponseBadRequest("Missing fields")
|
return HttpResponseBadRequest("Missing fields")
|
||||||
|
v = request.session.get("email_verify") or {}
|
||||||
|
if (v.get("email") or "") != email:
|
||||||
|
return JsonResponse({"ok": False, "message": "请先验证邮箱"}, status=400)
|
||||||
|
try:
|
||||||
|
exp_ts = int(v.get("expires_at") or 0)
|
||||||
|
except Exception:
|
||||||
|
exp_ts = 0
|
||||||
|
if exp_ts < int(time.time()):
|
||||||
|
return JsonResponse({"ok": False, "message": "验证码已过期"}, status=400)
|
||||||
|
if (v.get("code") or "") != email_code:
|
||||||
|
return JsonResponse({"ok": False, "message": "邮箱验证码错误"}, status=400)
|
||||||
rc = get_registration_code(code)
|
rc = get_registration_code(code)
|
||||||
if not rc:
|
if not rc:
|
||||||
return JsonResponse({"ok": False, "message": "注册码无效"}, status=400)
|
return JsonResponse({"ok": False, "message": "注册码无效"}, status=400)
|
||||||
@@ -217,4 +231,69 @@ def register_submit(request):
|
|||||||
})
|
})
|
||||||
if not ok:
|
if not ok:
|
||||||
return JsonResponse({"ok": False, "message": "注册失败"}, status=500)
|
return JsonResponse({"ok": False, "message": "注册失败"}, status=500)
|
||||||
return JsonResponse({"ok": True, "redirect_url": "/accounts/login/"})
|
try:
|
||||||
|
if "email_verify" in request.session:
|
||||||
|
del request.session["email_verify"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return JsonResponse({"ok": True, "redirect_url": "/accounts/login/"})
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
@csrf_protect
|
||||||
|
def send_email_code(request):
|
||||||
|
try:
|
||||||
|
payload = json.loads(request.body.decode("utf-8"))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return HttpResponseBadRequest("Invalid JSON")
|
||||||
|
email = (payload.get("email") or "").strip()
|
||||||
|
if not email:
|
||||||
|
return HttpResponseBadRequest("Missing email")
|
||||||
|
if "@" not in email:
|
||||||
|
return JsonResponse({"ok": False, "message": "邮箱格式不正确"}, status=400)
|
||||||
|
verify_code = "".join(random.choice(string.digits) for _ in range(6))
|
||||||
|
ttl = int(os.environ.get("SMTP_CODE_TTL", "600") or 600)
|
||||||
|
request.session["email_verify"] = {"email": email, "code": verify_code, "expires_at": int(time.time()) + max(60, ttl)}
|
||||||
|
ok, msg = _send_smtp_email(email, verify_code)
|
||||||
|
if not ok:
|
||||||
|
return JsonResponse({"ok": False, "message": msg or "验证码发送失败"}, status=500)
|
||||||
|
return JsonResponse({"ok": True})
|
||||||
|
|
||||||
|
def _send_smtp_email(to_email: str, code: str):
|
||||||
|
host = os.environ.get("SMTP_HOST", "")
|
||||||
|
port_raw = os.environ.get("SMTP_PORT", "")
|
||||||
|
try:
|
||||||
|
port = int(port_raw) if port_raw else 0
|
||||||
|
except Exception:
|
||||||
|
port = 0
|
||||||
|
user = os.environ.get("SMTP_USERNAME") or os.environ.get("SMTP_USER") or ""
|
||||||
|
password = os.environ.get("SMTP_PASSWORD", "")
|
||||||
|
use_tls = str(os.environ.get("SMTP_USE_TLS", "")).lower() in ("1", "true", "yes")
|
||||||
|
use_ssl = str(os.environ.get("SMTP_USE_SSL", "")).lower() in ("1", "true", "yes")
|
||||||
|
sender = os.environ.get("SMTP_FROM_EMAIL") or os.environ.get("SMTP_FROM") or user or ""
|
||||||
|
subject = os.environ.get("SMTP_SUBJECT") or "邮箱验证码"
|
||||||
|
if not host or not port or not sender:
|
||||||
|
return False, "缺少SMTP配置"
|
||||||
|
body = f"您的验证码是:{code},10分钟内有效。"
|
||||||
|
msg = f"From: {sender}\r\nTo: {to_email}\r\nSubject: {subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n{body}"
|
||||||
|
try:
|
||||||
|
if use_ssl:
|
||||||
|
server = smtplib.SMTP_SSL(host, port)
|
||||||
|
else:
|
||||||
|
server = smtplib.SMTP(host, port)
|
||||||
|
server.ehlo()
|
||||||
|
if use_tls and not use_ssl:
|
||||||
|
server.starttls()
|
||||||
|
server.ehlo()
|
||||||
|
if user and password:
|
||||||
|
server.login(user, password)
|
||||||
|
server.sendmail(sender, [to_email], msg.encode("utf-8"))
|
||||||
|
try:
|
||||||
|
server.quit()
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
server.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return True, ""
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
Reference in New Issue
Block a user