mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 08:23:20 +00:00
Merge branch 'master' into virusdefender-dev
* master: 修改错误提示措辞 精简重置密码邮件模板 添加重置密码以后的跳转到登录页面 修改一些细节问题 添加重置密码页面和js 修改申请重置密码页面及js名称 添加重置密码api url,调整url 修改申请找回用户登录信息的api逻辑,没有用户名也可申请 添加找回用户信息功能,修改邮件模板的一些细节 去掉申请重置密码的服务中要求填写用户名,因为有很多用户不记得用户名了 添加重置密码页面的url,并在用户登录页面添加url 添加重置密码页面的url 修改检测邮箱api使其可以被重置密码页面使用
This commit is contained in:
commit
b09305fdc1
@ -50,7 +50,6 @@ class EditUserSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class ApplyResetPasswordSerializer(serializers.Serializer):
|
class ApplyResetPasswordSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(max_length=30)
|
|
||||||
email = serializers.EmailField()
|
email = serializers.EmailField()
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||||
|
|
||||||
|
@ -148,17 +148,27 @@ class UsernameCheckAPIView(APIView):
|
|||||||
class EmailCheckAPIView(APIView):
|
class EmailCheckAPIView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""
|
"""
|
||||||
检测邮箱是否存在,存在返回状态码400,不存在返回200
|
检测邮箱是否存在,用状态码标识结果
|
||||||
---
|
---
|
||||||
"""
|
"""
|
||||||
|
#这里是为了适应前端表单验证空间的要求
|
||||||
|
reset = request.GET.get("reset", None)
|
||||||
|
#如果reset为true说明该请求是重置密码页面发出的,要返回的状态码应正好相反
|
||||||
|
if reset:
|
||||||
|
existed = 200
|
||||||
|
does_not_existed = 400
|
||||||
|
else:
|
||||||
|
existed = 400
|
||||||
|
does_not_existed = 200
|
||||||
|
|
||||||
email = request.GET.get("email", None)
|
email = request.GET.get("email", None)
|
||||||
if email:
|
if email:
|
||||||
try:
|
try:
|
||||||
User.objects.get(email=email)
|
User.objects.get(email=email)
|
||||||
return Response(status=400)
|
return Response(status=existed)
|
||||||
except Exception:
|
except Exception:
|
||||||
return Response(status=200)
|
return Response(status=does_not_existed)
|
||||||
return Response(status=200)
|
return Response(status=does_not_existed)
|
||||||
|
|
||||||
|
|
||||||
class UserAdminAPIView(APIView):
|
class UserAdminAPIView(APIView):
|
||||||
@ -273,7 +283,7 @@ class ApplyResetPasswordAPIView(APIView):
|
|||||||
if not captcha.check(data["captcha"]):
|
if not captcha.check(data["captcha"]):
|
||||||
return error_response(u"验证码错误")
|
return error_response(u"验证码错误")
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(username=data["username"], email=data["email"])
|
user = User.objects.get(email=data["email"])
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return error_response(u"用户不存在")
|
return error_response(u"用户不存在")
|
||||||
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
|
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
|
||||||
@ -285,14 +295,14 @@ class ApplyResetPasswordAPIView(APIView):
|
|||||||
|
|
||||||
email_template = email_template.replace("{{ username }}", user.username).\
|
email_template = email_template.replace("{{ username }}", user.username).\
|
||||||
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\
|
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\
|
||||||
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token)
|
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token)
|
||||||
|
|
||||||
send_email(settings.WEBSITE_INFO["website_name"],
|
send_email(settings.WEBSITE_INFO["website_name"],
|
||||||
user.email,
|
user.email,
|
||||||
user.username,
|
user.username,
|
||||||
settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件",
|
settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
|
||||||
email_template)
|
email_template)
|
||||||
return success_response(u"邮件发送成功")
|
return success_response(u"邮件发送成功,请前往您的邮箱查收")
|
||||||
else:
|
else:
|
||||||
return serializer_invalid_response(serializer)
|
return serializer_invalid_response(serializer)
|
||||||
|
|
||||||
@ -353,4 +363,14 @@ class SSOAPIView(APIView):
|
|||||||
token = rand_str()
|
token = rand_str()
|
||||||
request.user.auth_token = token
|
request.user.auth_token = token
|
||||||
request.user.save()
|
request.user.save()
|
||||||
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
|
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
|
||||||
|
|
||||||
|
|
||||||
|
def reset_password_page(request, token):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(reset_password_token=token)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return error_page(request, u"链接已失效")
|
||||||
|
if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60:
|
||||||
|
return error_page(request, u"链接已过期")
|
||||||
|
return render(request, "oj/account/reset_password.html", {"user": user})
|
||||||
|
10
oj/urls.py
10
oj/urls.py
@ -5,7 +5,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, ResetPasswordAPIView,
|
||||||
ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView)
|
ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView)
|
||||||
|
|
||||||
from announcement.views import AnnouncementAdminAPIView
|
from announcement.views import AnnouncementAdminAPIView
|
||||||
@ -122,12 +122,14 @@ urlpatterns = [
|
|||||||
|
|
||||||
url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
|
url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
|
||||||
|
|
||||||
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
url(r'^api/apply_reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||||
|
url(r'^api/reset_password/$', ResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||||
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
|
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
|
||||||
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
|
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
|
||||||
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
|
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
|
||||||
url('^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"),
|
url(r'^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"),
|
||||||
|
url(r'^reset_password/$', TemplateView.as_view(template_name="oj/account/apply_reset_password.html"), name="apply_reset_password_page"),
|
||||||
|
url(r'^reset_password/t/(?P<token>\w+)/$', "account.views.reset_password_page", name="reset_password_page")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
39
static/src/js/app/oj/account/applyResetPassword.js
Normal file
39
static/src/js/app/oj/account/applyResetPassword.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||||
|
var applied_captcha = false;
|
||||||
|
$('form').validator().on('submit', function (e) {
|
||||||
|
if (!e.isDefaultPrevented()) {
|
||||||
|
var email = $("#email").val();
|
||||||
|
var captcha = $("#captcha").val();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/apply_reset_password/",
|
||||||
|
data: {email: email, captcha: captcha},
|
||||||
|
dataType: "json",
|
||||||
|
method: "post",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
refresh_captcha();
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
refresh_captcha();
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(){
|
||||||
|
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function refresh_captcha(){
|
||||||
|
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||||
|
$("#captcha")[0].value = "";
|
||||||
|
}
|
||||||
|
$("#captcha-img").click(function(){
|
||||||
|
refresh_captcha();
|
||||||
|
});
|
||||||
|
});
|
41
static/src/js/app/oj/account/resetPassword.js
Normal file
41
static/src/js/app/oj/account/resetPassword.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||||
|
var applied_captcha = false;
|
||||||
|
$('form').validator().on('submit', function (e) {
|
||||||
|
if (!e.isDefaultPrevented()) {
|
||||||
|
var index = location.href.indexOf("/t/");
|
||||||
|
var token = location.href.substr(36+3, 32);
|
||||||
|
var captcha = $("#captcha").val();
|
||||||
|
var password = $("#new_password").val();
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/reset_password/",
|
||||||
|
data: {password: password, captcha: captcha, token:token},
|
||||||
|
dataType: "json",
|
||||||
|
method: "post",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
refresh_captcha();
|
||||||
|
bsAlert(data.data);
|
||||||
|
window.location.href = "/login/";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
refresh_captcha();
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(){
|
||||||
|
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function refresh_captcha(){
|
||||||
|
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||||
|
$("#captcha")[0].value = "";
|
||||||
|
}
|
||||||
|
$("#captcha-img").click(function(){
|
||||||
|
refresh_captcha();
|
||||||
|
});
|
||||||
|
});
|
40
template/src/oj/account/apply_reset_password.html
Normal file
40
template/src/oj/account/apply_reset_password.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% extends "oj_base.html" %}
|
||||||
|
{% block title %}
|
||||||
|
找回登录信息
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="container main">
|
||||||
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
<h2 class="text-center">找回登录信息</h2><br>
|
||||||
|
<div>
|
||||||
|
<p>请输入你注册时使用的邮箱地址,系统将自动向你的邮箱发送一封含有您登录信息的电子邮件,
|
||||||
|
你可以看到你的用户名,并可以选择重新设置登录密码,注意为了你的账户安全,重置密码链接仅在30分钟内有效</p>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<form id="login-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">注册电子邮件地址</label>
|
||||||
|
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址"
|
||||||
|
data-remote="/api/email_check/?reset=true" data-remote-error="该邮箱未被注册!" data-error="请填写正确的邮箱地址"
|
||||||
|
required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" id="captcha-area">
|
||||||
|
<label for="captcha">验证码</label> <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">
|
||||||
|
<button type="submit" class="btn btn-primary">提交</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block js_block %}
|
||||||
|
<script src="/static/js/app/oj/account/applyResetPassword.js"></script>
|
||||||
|
{% endblock %}
|
@ -36,7 +36,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="confirm_password">确认密码</label>
|
<label for="confirm_password">确认密码</label>
|
||||||
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
|
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
|
||||||
placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两个密码不一致" required>
|
placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两次密码不一致" required>
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
<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>
|
||||||
|
<a href="/reset_password/">忘记用户名/密码</a><br>
|
||||||
<a href="/register/">还没有帐号?点击注册</a>
|
<a href="/register/">还没有帐号?点击注册</a>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,10 +1,53 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "oj_base.html" %}
|
||||||
<html>
|
{% block title %}
|
||||||
<head lang="en">
|
找回登录信息
|
||||||
<meta charset="UTF-8">
|
{% endblock %}
|
||||||
<title></title>
|
{% block body %}
|
||||||
</head>
|
<div class="container main">
|
||||||
<body>
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
<h2 class="text-center">找回登录信息</h2><br>
|
||||||
|
<br>
|
||||||
|
|
||||||
</body>
|
<form id="login-form">
|
||||||
</html>
|
<div class="form-group">
|
||||||
|
<label for="email">注册电子邮件地址</label>
|
||||||
|
<input type="text" class="form-control input-lg" name="email" value="{{ user.email }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">用户名</label>
|
||||||
|
<input type="text" class="form-control input-lg" name="username" value="{{ user.username }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new_password">新密码</label>
|
||||||
|
<input type="password" class="form-control input-lg" id="new_password" name="new_password"
|
||||||
|
placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirm_password">确认密码</label>
|
||||||
|
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
|
||||||
|
placeholder="确认密码" maxlength="30" data-match="#new_password" data-error="请输入确认密码"
|
||||||
|
data-match-error="两次密码不一致"
|
||||||
|
required>
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" id="captcha-area">
|
||||||
|
<label for="captcha">验证码</label> <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">
|
||||||
|
<button type="submit" class="btn btn-primary">提交</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block js_block %}
|
||||||
|
<script src="/static/js/app/oj/account/resetPassword.js"></script>
|
||||||
|
{% endblock %}
|
@ -8,7 +8,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr height="39" style="background-color:#50a5e6;">
|
<tr height="39" style="background-color:#50a5e6;">
|
||||||
<td style="padding-left:15px;font-family:'微软雅黑','黑体',arial;">
|
<td style="padding-left:15px;font-family:'微软雅黑','黑体',arial;">
|
||||||
{{ website_name }} 密码找回邮件
|
{{ website_name }} 登录信息找回
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -32,12 +32,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr height="30">
|
<tr height="30">
|
||||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
||||||
您刚刚在 {{ website_name }} 使用了找回密码功能。
|
您刚刚在 {{ website_name }} 申请了找回登录信息服务。
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr height="30">
|
<tr height="30">
|
||||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
|
||||||
请在<span style="color:rgb(255,0,0)">60分钟</span>内点击下面链接设置您的新密码:
|
请在<span style="color:rgb(255,0,0)">30分钟</span>内点击下面链接设置您的新密码:
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr height="60">
|
<tr height="60">
|
||||||
@ -63,7 +63,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr height="20">
|
<tr height="20">
|
||||||
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:12px;">
|
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:12px;">
|
||||||
如果你没有提出过密码修改申请,请忽略此邮件。有可能是其他用户误填了你的用户名。我们不会对你的帐户进行任何修改。
|
如果您没有提出过该申请,请忽略此邮件。有可能是其他用户误填了您的邮件地址,我们不会对你的帐户进行任何修改。
|
||||||
|
请不要向他人透露本邮件的内容,否则可能会导致您的账号被盗。
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr height="20">
|
<tr height="20">
|
||||||
|
Loading…
Reference in New Issue
Block a user