diff --git a/account/serializers.py b/account/serializers.py index 4cda3ae0..f60e9cac 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -7,6 +7,7 @@ from .models import User class UserLoginSerializer(serializers.Serializer): username = 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): @@ -22,6 +23,7 @@ class UserRegisterSerializer(serializers.Serializer): real_name = serializers.CharField(max_length=30) password = serializers.CharField(max_length=30, min_length=6) email = serializers.EmailField(max_length=254) + captcha = serializers.CharField(max_length=4, min_length=4) class UserChangePasswordSerializer(serializers.Serializer): diff --git a/account/views.py b/account/views.py index 4aa326a9..8a3414f3 100644 --- a/account/views.py +++ b/account/views.py @@ -5,15 +5,15 @@ from django.shortcuts import render from django.db.models import Q 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.captcha import Captcha from .decorators import login_required from .models import User from .serializers import (UserLoginSerializer, UsernameCheckSerializer, - UserRegisterSerializer, UserChangePasswordSerializer, - EmailCheckSerializer, UserSerializer, EditUserSerializer) + UserRegisterSerializer, UserChangePasswordSerializer, + EmailCheckSerializer, UserSerializer, EditUserSerializer) class UserLoginAPIView(APIView): @@ -26,6 +26,14 @@ class UserLoginAPIView(APIView): serializer = UserLoginSerializer(data=request.data) if serializer.is_valid(): 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"]) # 用户名或密码错误的话 返回None if user: @@ -64,6 +72,9 @@ class UserRegisterAPIView(APIView): serializer = UserRegisterSerializer(data=request.data) if serializer.is_valid(): data = serializer.data + captcha = Captcha(request) + if not captcha.check(data["captcha"]): + return error_response(u"验证码错误") try: User.objects.get(username=data["username"]) return error_response(u"用户名已存在") @@ -109,39 +120,35 @@ class UserChangePasswordAPIView(APIView): class UsernameCheckAPIView(APIView): - def post(self, request): + def get(self, request): """ - 检测用户名是否存在,存在返回True,不存在返回False + 检测用户名是否存在,存在返回状态码400,不存在返回200 --- - request_serializer: UsernameCheckSerializer """ - serializer = UsernameCheckSerializer(data=request.data) - if serializer.is_valid(): + username = request.GET.get("username", None) + if username: try: - User.objects.get(username=serializer.data["username"]) - return success_response(True) + User.objects.get(username=username) + return Response(status=400) except User.DoesNotExist: - return success_response(False) - else: - return serializer_invalid_response(serializer) + return Response(status=200) + return Response(status=200) class EmailCheckAPIView(APIView): - def post(self, request): + def get(self, request): """ - 检测邮箱是否存在,存在返回True,不存在返回False + 检测邮箱是否存在,存在返回状态码400,不存在返回200 --- - request_serializer: EmailCheckSerializer """ - serializer = EmailCheckSerializer(data=request.data) - if serializer.is_valid(): + email = request.GET.get("email", None) + if email: try: - User.objects.get(email=serializer.data["email"]) - return success_response(True) + User.objects.get(email=email) + return Response(status=400) except User.DoesNotExist: - return success_response(False) - else: - return serializer_invalid_response(serializer) + return Response(status=200) + return Response(status=200) class UserAdminAPIView(APIView): @@ -206,3 +213,19 @@ class UserInfoAPIView(APIView): response_serializer: UserSerializer """ 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}) diff --git a/oj/urls.py b/oj/urls.py index d9373bb9..e1908712 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -4,7 +4,7 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, - UserAdminAPIView, UserInfoAPIView) + UserAdminAPIView, UserInfoAPIView, AccountSecurityAPIView) from announcement.views import AnnouncementAdminAPIView @@ -115,4 +115,5 @@ urlpatterns = [ 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'^api/account_security_check/$', AccountSecurityAPIView.as_view(), name="account_security_check"), ] diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index 0a057627..32343c95 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -1,26 +1,31 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) { + var applied_captcha = false; $('form').validator().on('submit', function (e) { if (!e.isDefaultPrevented()) { var username = $("#username").val(); var password = $("#password").val(); + var ajaxData = {username: username, password: password}; + if (applied_captcha) { + ajaxData.captcha = $("#captcha").val(); + } $.ajax({ beforeSend: csrfTokenHeader, url: "/api/login/", - data: {username: username, password: password}, + data: ajaxData, dataType: "json", method: "post", success: function (data) { if (!data.code) { //成功登陆 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 = "/"; return; } // 判断来源,只有同域下才跳转 - if(ref.split("/")[2].split(":")[0] == location.hostname){ + if (ref.split("/")[2].split(":")[0] == location.hostname) { location.href = ref; return; } @@ -28,6 +33,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c location.href = "/"; } else { + refresh_captcha(); bsAlert(data.data); } } @@ -35,5 +41,34 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c }); 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('  

'); + 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(); + }); }); \ No newline at end of file diff --git a/static/src/js/app/oj/account/register.js b/static/src/js/app/oj/account/register.js index 9537fdbc..e5d22d5b 100644 --- a/static/src/js/app/oj/account/register.js +++ b/static/src/js/app/oj/account/register.js @@ -5,10 +5,11 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c var realName = $("#real_name").val(); var password = $("#password").val(); var email = $("#email").val(); + var captcha = $("#captcha").val(); $.ajax({ beforeSend: csrfTokenHeader, 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", method: "post", success: function (data) { @@ -16,11 +17,20 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c window.location.href = "/login/"; } else { + refresh_captcha(); bsAlert(data.data); } } }); return false; } - }) + }); + function refresh_captcha() { + $("#captcha-img")[0].src = "/captcha/?" + Math.random(); + $("#captcha")[0].value = ""; + } + + $("#captcha-img").click(function () { + refresh_captcha(); + }); }); \ No newline at end of file diff --git a/template/src/oj/account/change_password.html b/template/src/oj/account/change_password.html index ab9200db..0ed8fdc0 100644 --- a/template/src/oj/account/change_password.html +++ b/template/src/oj/account/change_password.html @@ -21,9 +21,10 @@
- - - + +    +

+
diff --git a/template/src/oj/account/login.html b/template/src/oj/account/login.html index 5777fd3f..b09b0f1a 100644 --- a/template/src/oj/account/login.html +++ b/template/src/oj/account/login.html @@ -19,6 +19,7 @@
+
diff --git a/template/src/oj/account/register.html b/template/src/oj/account/register.html index ef242fb4..ccb09022 100644 --- a/template/src/oj/account/register.html +++ b/template/src/oj/account/register.html @@ -7,7 +7,7 @@
- +
@@ -17,7 +17,7 @@
- +
@@ -31,6 +31,12 @@ placeholder="确认密码" data-match="#password" data-match-error="两个密码不一致" data-error="请填写确认密码" required>
+
+ +   

+ +
+
diff --git a/template/src/oj/contest/contest_problem.html b/template/src/oj/contest/contest_problem.html index 06dad2c6..69bec1e3 100644 --- a/template/src/oj/contest/contest_problem.html +++ b/template/src/oj/contest/contest_problem.html @@ -2,107 +2,97 @@ {% block body %}
- -

{{ contest_problem.title }}

+ +

{{ contest_problem.title }}

-

发布时间 : {{ contest_problem.create_time }}   - 时间限制 : {{ contest_problem.time_limit }}ms   - 内存限制 : {{ contest_problem.memory_limit }}M -

- -
-
- - -
{{ contest_problem.description|safe }}
-
-
- - -

{{ contest_problem.input_description }}

+

发布时间 : {{ contest_problem.create_time }}   + 时间限制 : {{ contest_problem.time_limit }}ms   + 内存限制 : {{ contest_problem.memory_limit }}M +

-
- + -

{{ contest_problem.output_description }}

+
{{ contest_problem.description|safe }}
- {% for item in samples %} -
- - 复制 -
-{{ item.input }}
+
+ -
-
+

{{ contest_problem.input_description }}

- -
-{{ item.output }}
-
- {% endfor %} - {% if problem.hint %} -
- - -

{{ contest_problem.hint|safe }}

-
- {% endif %} - {% if contest_problem.hint %} -
- - -
{{ contest_problem.hint|safe }}
-
- {% endif %} - - {% ifequal contest.status 0 %}
- -
- - - +
+ + +

{{ contest_problem.output_description }}

+ {% for item in samples %} +
+ + 复制 +
{{ item.input }}
+
+
+ +
{{ item.output }}
+
+ {% endfor %} + {% if problem.hint %} +
+ +

{{ contest_problem.hint|safe }}

+
+ {% endif %} + {% if contest_problem.hint %} +
+ +
{{ contest_problem.hint|safe }}
+
+ {% endif %} + {% ifequal contest.status 0 %} +
+ +
+ + + +
+
+
+ + +
+
+
+ + +
+ {% endifequal %} + {% if show_warning %} + + {% endif %} +
+
+
- -
- - -
-
- -
- - - -
- {% endifequal %} - - {% if show_warning %} - - {% endif %} - -
-
{% endblock %}