Accept Merge Request #246 登录注册验证码,注册页面用户名,邮箱ajax验证 : (dev-sxw -> dev)

Merge Request: 登录注册验证码,注册页面用户名,邮箱ajax验证
Created By: @esp
Reviewed By: @virusdefender 
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/246
This commit is contained in:
virusdefender 2015-09-17 13:30:00 +08:00
commit 3d630a4efd
9 changed files with 193 additions and 124 deletions

View File

@ -7,6 +7,7 @@ from .models import User
class UserLoginSerializer(serializers.Serializer): class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30) username = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30)
captcha = serializers.CharField(required=False,min_length=4,max_length=4)
class UsernameCheckSerializer(serializers.Serializer): class UsernameCheckSerializer(serializers.Serializer):
@ -22,6 +23,7 @@ class UserRegisterSerializer(serializers.Serializer):
real_name = serializers.CharField(max_length=30) real_name = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=30, min_length=6) password = serializers.CharField(max_length=30, min_length=6)
email = serializers.EmailField(max_length=254) email = serializers.EmailField(max_length=254)
captcha = serializers.CharField(max_length=4, min_length=4)
class UserChangePasswordSerializer(serializers.Serializer): class UserChangePasswordSerializer(serializers.Serializer):

View File

@ -5,15 +5,15 @@ from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response
from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate from utils.shortcuts import serializer_invalid_response, error_response, success_response, paginate
from utils.captcha import Captcha from utils.captcha import Captcha
from .decorators import login_required from .decorators import login_required
from .models import User from .models import User
from .serializers import (UserLoginSerializer, UsernameCheckSerializer, from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
UserRegisterSerializer, UserChangePasswordSerializer, UserRegisterSerializer, UserChangePasswordSerializer,
EmailCheckSerializer, UserSerializer, EditUserSerializer) EmailCheckSerializer, UserSerializer, EditUserSerializer)
class UserLoginAPIView(APIView): class UserLoginAPIView(APIView):
@ -26,6 +26,14 @@ class UserLoginAPIView(APIView):
serializer = UserLoginSerializer(data=request.data) serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
data = serializer.data data = serializer.data
user = User.objects.get(username=data["username"])
# 只有管理员才适用验证码登录
if user.admin_type > 0:
if not "captcha" in data:
return error_response(u"请填写验证码!")
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
user = auth.authenticate(username=data["username"], password=data["password"]) user = auth.authenticate(username=data["username"], password=data["password"])
# 用户名或密码错误的话 返回None # 用户名或密码错误的话 返回None
if user: if user:
@ -64,6 +72,9 @@ class UserRegisterAPIView(APIView):
serializer = UserRegisterSerializer(data=request.data) serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
data = serializer.data data = serializer.data
captcha = Captcha(request)
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
try: try:
User.objects.get(username=data["username"]) User.objects.get(username=data["username"])
return error_response(u"用户名已存在") return error_response(u"用户名已存在")
@ -109,39 +120,35 @@ class UserChangePasswordAPIView(APIView):
class UsernameCheckAPIView(APIView): class UsernameCheckAPIView(APIView):
def post(self, request): def get(self, request):
""" """
检测用户名是否存在存在返回True不存在返回False 检测用户名是否存在存在返回状态码400不存在返回200
--- ---
request_serializer: UsernameCheckSerializer
""" """
serializer = UsernameCheckSerializer(data=request.data) username = request.GET.get("username", None)
if serializer.is_valid(): if username:
try: try:
User.objects.get(username=serializer.data["username"]) User.objects.get(username=username)
return success_response(True) return Response(status=400)
except User.DoesNotExist: except User.DoesNotExist:
return success_response(False) return Response(status=200)
else: return Response(status=200)
return serializer_invalid_response(serializer)
class EmailCheckAPIView(APIView): class EmailCheckAPIView(APIView):
def post(self, request): def get(self, request):
""" """
检测邮箱是否存在存在返回True不存在返回False 检测邮箱是否存在存在返回状态码400不存在返回200
--- ---
request_serializer: EmailCheckSerializer
""" """
serializer = EmailCheckSerializer(data=request.data) email = request.GET.get("email", None)
if serializer.is_valid(): if email:
try: try:
User.objects.get(email=serializer.data["email"]) User.objects.get(email=email)
return success_response(True) return Response(status=400)
except User.DoesNotExist: except User.DoesNotExist:
return success_response(False) return Response(status=200)
else: return Response(status=200)
return serializer_invalid_response(serializer)
class UserAdminAPIView(APIView): class UserAdminAPIView(APIView):
@ -206,3 +213,19 @@ class UserInfoAPIView(APIView):
response_serializer: UserSerializer response_serializer: UserSerializer
""" """
return success_response(UserSerializer(request.user).data) return success_response(UserSerializer(request.user).data)
class AccountSecurityAPIView(APIView):
def get(self, request):
"""
判断用户登录是否需要验证码
---
"""
username = request.GET.get("username", None)
if username:
try:
User.objects.get(username=username, admin_type__gt=0)
except User.DoesNotExist:
return success_response({"applied_captcha": False})
return success_response({"applied_captcha": True})
return success_response({"applied_captcha": False})

View File

@ -4,7 +4,7 @@ from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView, UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView) UserAdminAPIView, UserInfoAPIView, AccountSecurityAPIView)
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
@ -115,4 +115,5 @@ urlpatterns = [
url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"), url(r'^api/submission/share/$', SubmissionShareAPIView.as_view(), name="submission_share_api"),
url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"), url(r'^captcha/$', "utils.captcha.views.show_captcha", name="show_captcha"),
url(r'^api/account_security_check/$', AccountSecurityAPIView.as_view(), name="account_security_check"),
] ]

View File

@ -1,26 +1,31 @@
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) { require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
var applied_captcha = false;
$('form').validator().on('submit', function (e) { $('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) { if (!e.isDefaultPrevented()) {
var username = $("#username").val(); var username = $("#username").val();
var password = $("#password").val(); var password = $("#password").val();
var ajaxData = {username: username, password: password};
if (applied_captcha) {
ajaxData.captcha = $("#captcha").val();
}
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/login/", url: "/api/login/",
data: {username: username, password: password}, data: ajaxData,
dataType: "json", dataType: "json",
method: "post", method: "post",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
//成功登陆 //成功登陆
var ref = document.referrer; var ref = document.referrer;
if(ref){ if (ref) {
// 注册页和本页的来源的跳转回首页,防止死循环 // 注册页和本页的来源的跳转回首页,防止死循环
if(ref.indexOf("register") > -1 || ref.indexOf("login") > -1){ if (ref.indexOf("register") > -1 || ref.indexOf("login") > -1) {
location.href = "/"; location.href = "/";
return; return;
} }
// 判断来源,只有同域下才跳转 // 判断来源,只有同域下才跳转
if(ref.split("/")[2].split(":")[0] == location.hostname){ if (ref.split("/")[2].split(":")[0] == location.hostname) {
location.href = ref; location.href = ref;
return; return;
} }
@ -28,6 +33,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
location.href = "/"; location.href = "/";
} }
else { else {
refresh_captcha();
bsAlert(data.data); bsAlert(data.data);
} }
} }
@ -35,5 +41,34 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
}); });
return false; return false;
} }
}) });
$('#username').blur(function () {
if ($("#username").val()) {
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/account_security_check/?username=" + $("#username").val(),
method: "get",
success: function (data) {
if (!data.code) {
if (data.data.applied_captcha) {
$('#captcha-area').html('<label for="captcha">验证码</label>&nbsp;&nbsp;<img src="/captcha/" id="captcha-img"><small><p></p></small><input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required><div class="help-block with-errors"></div>');
applied_captcha = true;
}
else {
$('#captcha-area').html('');
applied_captcha = false;
}
}
}
});
}
});
function refresh_captcha(){
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function(){
refresh_captcha();
});
}); });

View File

@ -5,10 +5,11 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
var realName = $("#real_name").val(); var realName = $("#real_name").val();
var password = $("#password").val(); var password = $("#password").val();
var email = $("#email").val(); var email = $("#email").val();
var captcha = $("#captcha").val();
$.ajax({ $.ajax({
beforeSend: csrfTokenHeader, beforeSend: csrfTokenHeader,
url: "/api/register/", url: "/api/register/",
data: {username: username, real_name: realName, password: password, email: email}, data: {username: username, real_name: realName, password: password, email: email, captcha:captcha},
dataType: "json", dataType: "json",
method: "post", method: "post",
success: function (data) { success: function (data) {
@ -16,11 +17,20 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
window.location.href = "/login/"; window.location.href = "/login/";
} }
else { else {
refresh_captcha();
bsAlert(data.data); bsAlert(data.data);
} }
} }
}); });
return false; return false;
} }
}) });
function refresh_captcha() {
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function () {
refresh_captcha();
});
}); });

View File

@ -21,9 +21,10 @@
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="confirm_password">验证码</label> <label for="captcha">验证码</label>
<img src="/captcha/" id="captcha-img"> &nbsp;&nbsp;<img src="/captcha/" id="captcha-img">
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" required> <small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -19,6 +19,7 @@
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group" id="captcha-area"></div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">提交</button> <button type="submit" class="btn btn-primary">提交</button>
</div> </div>

View File

@ -7,7 +7,7 @@
<form id="register-form"> <form id="register-form">
<div class="form-group"> <div class="form-group">
<label for="username">用户名</label> <label for="username">用户名</label>
<input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-error="请填写用户名" required autofocus> <input type="text" class="form-control input-lg" id="username" name="username" placeholder="用户名" data-remote="/api/username_check/" data-remote-error="该用户名已被注册!" data-error="请填写用户名" required autofocus>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -17,7 +17,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">邮箱地址</label> <label for="email">邮箱地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-error="请填写正确的邮箱地址" required> <input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" data-error="请填写正确的邮箱地址" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -31,6 +31,12 @@
placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required> placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
<div class="form-group">
<label for="captcha">验证码</label>
&nbsp;&nbsp;<img src="/captcha/" id="captcha-img"><small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">提交</button> <button type="submit" class="btn btn-primary">提交</button>
</div> </div>

View File

@ -2,107 +2,97 @@
{% block body %} {% block body %}
<div class="container main"> <div class="container main">
<ul class="nav nav-tabs nav-tabs-google"> <ul class="nav nav-tabs nav-tabs-google">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li> <a href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/">题目</a></li>
<li role="presentation"><a <li role="presentation"><a
href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/submissions/">我的提交</a> href="/contest/{{ contest_problem.contest.id }}/problem/{{ contest_problem.id }}/submissions/">我的提交</a>
</li> </li>
</ul> </ul>
<h2 class="text-center">{{ contest_problem.title }}</h2> <h2 class="text-center">{{ contest_problem.title }}</h2>
<p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp; <p class="text-muted text-center">发布时间 : {{ contest_problem.create_time }}&nbsp;&nbsp;
时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp; 时间限制 : {{ contest_problem.time_limit }}ms&nbsp;&nbsp;
内存限制 : {{ contest_problem.memory_limit }}M 内存限制 : {{ contest_problem.memory_limit }}M
</p> </p>
<div>
<div class="problem-section">
<label class="problem-label">描述</label>
<div class="problem-detail">{{ contest_problem.description|safe }}</div>
</div>
<div class="problem-section">
<label class="problem-label">输入</label>
<p class="problem-detail">{{ contest_problem.input_description }}</p>
<div> <div>
<div class="problem-section"> <div class="problem-section">
<label class="problem-label">输出</label> <label class="problem-label">描述</label>
<p class="problem-detail">{{ contest_problem.output_description }}</p> <div class="problem-detail">{{ contest_problem.description|safe }}</div>
</div> </div>
{% for item in samples %} <div class="problem-section">
<div class="problem-section"> <label class="problem-label">输入</label>
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<a href="javascript:void(0)" class="copy-sample" data-clipboard-text="{{ item.input }}">复制</a>
<pre>
{{ item.input }}</pre>
</div> <p class="problem-detail">{{ contest_problem.input_description }}</p>
<div class="problem-section">
<label class="problem-label">样例输出{{ forloop.counter }}</label>
<pre>
{{ item.output }}</pre>
</div>
{% endfor %}
{% if problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
{% if contest_problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<div class="problem-detail">{{ contest_problem.hint|safe }}</div>
</div>
{% endif %}
{% ifequal contest.status 0 %}
<div> <div>
<label>选择语言</label>
<div> <div class="problem-section">
<label class="radio-inline"> <label class="problem-label">输出</label>
<input type="radio" name="language" value="1" checked> C (GCC 4.8)
</label> <p class="problem-detail">{{ contest_problem.output_description }}</p>
<label class="radio-inline">
<input type="radio" name="language" value="2"> C++ (G++ 4.3)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="3"> Java (Oracle JDK 1.7)
</label>
</div> </div>
{% for item in samples %}
<div class="problem-section">
<label class="problem-label">样例输入{{ forloop.counter }}</label>
<a href="javascript:void(0)" class="copy-sample"
data-clipboard-text="{{ item.input }}">复制</a>
<pre>{{ item.input }}</pre>
</div>
<div class="problem-section">
<label class="problem-label">样例输出{{ forloop.counter }}</label>
<pre>{{ item.output }}</pre>
</div>
{% endfor %}
{% if problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<p class="problem-detail">{{ contest_problem.hint|safe }}</p>
</div>
{% endif %}
{% if contest_problem.hint %}
<div class="problem-section hide">
<label class="problem-label">提示</label>
<div class="problem-detail">{{ contest_problem.hint|safe }}</div>
</div>
{% endif %}
{% ifequal contest.status 0 %}
<div>
<label>选择语言</label>
<div>
<label class="radio-inline">
<input type="radio" name="language" value="1" checked> C (GCC 4.8)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="2"> C++ (G++ 4.3)
</label>
<label class="radio-inline">
<input type="radio" name="language" value="3"> Java (Oracle JDK 1.7)
</label>
</div>
</div>
<div id="code-field">
<label class="problem-label">提交代码</label>
<textarea id="code-editor"></textarea>
</div>
<hr>
<div id="submit-code">
<button type="button" class="btn btn-primary" id="submit-code-button">
提交代码
</button>
<img src="/static/img/loading.gif" id="loading-gif">
</div>
{% endifequal %}
{% if show_warning %}
<div class="alert alert-success" role="alert">{{ warning }}</div>
{% endif %}
<div id="result">
</div>
<hr>
</div> </div>
<div id="code-field">
<label class="problem-label">提交代码</label>
<textarea id="code-editor"></textarea>
</div>
<hr>
<div id="submit-code">
<button type="button" class="btn btn-primary" id="submit-code-button">
提交代码
</button>
<img src="/static/img/loading.gif" id="loading-gif">
</div>
{% endifequal %}
{% if show_warning %}
<div class="alert alert-success" role="alert">{{ warning }}</div>
{% endif %}
<div id="result">
</div> </div>
<hr>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}