Skip to content

Commit 7e96de0

Browse files
authored
Merge pull request #508 from liangliangyy/dev
增加新功能
2 parents 7b54a95 + 5c60845 commit 7e96de0

File tree

18 files changed

+427
-104
lines changed

18 files changed

+427
-104
lines changed

DjangoBlog/blog_signals.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,23 @@
1212
@file: blog_signals.py
1313
@time: 2017/8/12 上午10:18
1414
"""
15-
import django
15+
import _thread
16+
import logging
17+
1618
import django.dispatch
1719
from django.dispatch import receiver
1820
from django.conf import settings
1921
from django.contrib.admin.models import LogEntry
20-
from DjangoBlog.utils import get_current_site
2122
from django.core.mail import EmailMultiAlternatives
2223
from django.db.models.signals import post_save
23-
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
24+
from django.contrib.auth.signals import user_logged_in, user_logged_out
2425

25-
from DjangoBlog.utils import cache, send_email, expire_view_cache, delete_sidebar_cache, delete_view_cache
26-
from DjangoBlog.spider_notify import SpiderNotify
2726
from oauth.models import OAuthUser
28-
from blog.models import Article, Category, Tag, Links, SideBar, BlogSettings
2927
from comments.models import Comment
3028
from comments.utils import send_comment_email
31-
import _thread
32-
import logging
29+
from DjangoBlog.utils import get_current_site
30+
from DjangoBlog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache
31+
from DjangoBlog.spider_notify import SpiderNotify
3332

3433
logger = logging.getLogger(__name__)
3534

@@ -61,7 +60,7 @@ def send_email_signal_handler(sender, **kwargs):
6160
result = msg.send()
6261
log.send_result = result > 0
6362
except Exception as e:
64-
logger.error(e)
63+
logger.error(f"失败邮箱号: {emailto}, {e}")
6564
log.send_result = False
6665
log.save()
6766

DjangoBlog/utils.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,21 @@
22
# encoding: utf-8
33

44

5-
"""
6-
@version: ??
7-
@author: liangliangyy
8-
@license: MIT Licence
9-
@contact: liangliangyy@gmail.com
10-
@site: https://www.lylinux.net/
11-
@software: PyCharm
12-
@file: utils.py
13-
@time: 2017/1/19 上午2:30
14-
"""
15-
from django.core.cache import cache
16-
from django.contrib.sites.models import Site
5+
import logging
6+
import os
7+
import random
8+
import string
9+
import uuid
1710
from hashlib import sha256
11+
1812
import mistune
13+
import requests
14+
from django.contrib.sites.models import Site
15+
from django.core.cache import cache
1916
from mistune import escape, escape_link
2017
from pygments import highlight
21-
from pygments.lexers import get_lexer_by_name
2218
from pygments.formatters import html
23-
import logging
24-
import requests
25-
import uuid
26-
import os
19+
from pygments.lexers import get_lexer_by_name
2720

2821
logger = logging.getLogger(__name__)
2922

@@ -187,6 +180,11 @@ def send_email(emailto, title, content):
187180
content=content)
188181

189182

183+
def generate_code() -> str:
184+
"""生成随机数验证码"""
185+
return ''.join(random.sample(string.digits, 6))
186+
187+
190188
def parse_dict_to_url(dict):
191189
from urllib.parse import quote
192190
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))

accounts/forms.py

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
@file: forms.py
1313
@time: 2016/11/20 下午3:16
1414
"""
15+
from django import forms
16+
from django.contrib.auth import get_user_model, password_validation
1517
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
16-
from django.forms import widgets
17-
from django.conf import settings
18-
from django.contrib.auth import get_user_model
1918
from django.core.exceptions import ValidationError
19+
from django.forms import widgets
20+
21+
from . import utils
22+
from .models import BlogUser
2023

2124

2225
class LoginForm(AuthenticationForm):
@@ -50,3 +53,79 @@ def clean_email(self):
5053
class Meta:
5154
model = get_user_model()
5255
fields = ("username", "email")
56+
57+
58+
class ForgetPasswordForm(forms.Form):
59+
new_password1 = forms.CharField(
60+
label="新密码",
61+
widget=forms.PasswordInput(
62+
attrs={
63+
"class": "form-control",
64+
'placeholder': "密码"
65+
}
66+
),
67+
)
68+
69+
new_password2 = forms.CharField(
70+
label="确认密码",
71+
widget=forms.PasswordInput(
72+
attrs={
73+
"class": "form-control",
74+
'placeholder': "确认密码"
75+
}
76+
),
77+
)
78+
79+
email = forms.EmailField(
80+
label='邮箱',
81+
widget=forms.TextInput(
82+
attrs={
83+
'class': 'form-control',
84+
'placeholder': "邮箱"
85+
}
86+
),
87+
)
88+
89+
code = forms.CharField(
90+
label='验证码',
91+
widget=forms.TextInput(
92+
attrs={
93+
'class': 'form-control',
94+
'placeholder': "验证码"
95+
}
96+
),
97+
)
98+
99+
def clean_new_password2(self):
100+
password1 = self.data.get("new_password1")
101+
password2 = self.data.get("new_password2")
102+
if password1 and password2 and password1 != password2:
103+
raise ValidationError("两次密码不一致")
104+
password_validation.validate_password(password2)
105+
106+
return password2
107+
108+
def clean_email(self):
109+
user_email = self.cleaned_data.get("email")
110+
if not BlogUser.objects.filter(
111+
email=user_email
112+
).exists():
113+
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
114+
raise ValidationError("未找到邮箱对应的用户")
115+
return user_email
116+
117+
def clean_code(self):
118+
code = self.cleaned_data.get("code")
119+
error = utils.verify(
120+
email=self.cleaned_data.get("email"),
121+
code=code,
122+
)
123+
if error:
124+
raise ValidationError(error)
125+
return code
126+
127+
128+
class ForgetPasswordCodeForm(forms.Form):
129+
email = forms.EmailField(
130+
label="邮箱号"
131+
)

accounts/tests.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1+
from django.conf import settings
12
from django.test import Client, RequestFactory, TestCase
2-
from blog.models import Article, Category, Tag
3-
from django.contrib.auth import get_user_model
4-
from DjangoBlog.utils import delete_view_cache, delete_sidebar_cache
5-
from accounts.models import BlogUser
63
from django.urls import reverse
7-
from DjangoBlog.utils import *
8-
from django.conf import settings
94
from django.utils import timezone
105

6+
from DjangoBlog.utils import *
7+
from accounts.models import BlogUser
8+
from blog.models import Article, Category
9+
from . import utils
10+
1111

1212
# Create your tests here.
1313

1414
class AccountTest(TestCase):
1515
def setUp(self):
1616
self.client = Client()
1717
self.factory = RequestFactory()
18+
self.blog_user = BlogUser.objects.create_user(
19+
username="test",
20+
email="admin@admin.com",
21+
password="12345678"
22+
)
23+
self.new_test = "xxx123--="
1824

1925
def test_validate_account(self):
2026
site = get_current_site().domain
@@ -111,3 +117,101 @@ def test_validate_register(self):
111117

112118
response = self.client.get(article.get_admin_url())
113119
self.assertIn(response.status_code, [301, 302, 200])
120+
121+
def test_verify_email_code(self):
122+
to_email = "admin@admin.com"
123+
code = generate_code()
124+
utils.set_code(to_email, code)
125+
utils.send_verify_email(to_email, code)
126+
127+
err = utils.verify("admin@admin.com", code)
128+
self.assertEqual(err, None)
129+
130+
err = utils.verify("admin@123.com", code)
131+
self.assertEqual(type(err), str)
132+
133+
def test_forget_password_email_code_success(self):
134+
resp = self.client.post(
135+
path=reverse("account:forget_password_code"),
136+
data=dict(email="admin@admin.com")
137+
)
138+
139+
self.assertEqual(resp.status_code, 200)
140+
self.assertEqual(resp.content.decode("utf-8"), "ok")
141+
142+
def test_forget_password_email_code_fail(self):
143+
resp = self.client.post(
144+
path=reverse("account:forget_password_code"),
145+
data=dict()
146+
)
147+
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
148+
149+
resp = self.client.post(
150+
path=reverse("account:forget_password_code"),
151+
data=dict(email="admin@com")
152+
)
153+
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
154+
155+
def test_forget_password_email_success(self):
156+
code = generate_code()
157+
utils.set_code(self.blog_user.email, code)
158+
data = dict(
159+
new_password1=self.new_test,
160+
new_password2=self.new_test,
161+
email=self.blog_user.email,
162+
code=code,
163+
)
164+
resp = self.client.post(
165+
path=reverse("account:forget_password"),
166+
data=data
167+
)
168+
self.assertEqual(resp.status_code, 302)
169+
170+
# 验证用户密码是否修改成功
171+
blog_user = BlogUser.objects.filter(
172+
email=self.blog_user.email,
173+
).first() # type: BlogUser
174+
self.assertNotEqual(blog_user, None)
175+
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
176+
177+
def test_forget_password_email_not_user(self):
178+
data = dict(
179+
new_password1=self.new_test,
180+
new_password2=self.new_test,
181+
email="123@123.com",
182+
code="123456",
183+
)
184+
resp = self.client.post(
185+
path=reverse("account:forget_password"),
186+
data=data
187+
)
188+
189+
self.assertEqual(resp.status_code, 200)
190+
self.assertFormError(
191+
response=resp,
192+
form="form",
193+
field="email",
194+
errors="未找到邮箱对应的用户"
195+
)
196+
197+
def test_forget_password_email_code_error(self):
198+
code = generate_code()
199+
utils.set_code(self.blog_user.email, code)
200+
data = dict(
201+
new_password1=self.new_test,
202+
new_password2=self.new_test,
203+
email=self.blog_user.email,
204+
code="111111",
205+
)
206+
resp = self.client.post(
207+
path=reverse("account:forget_password"),
208+
data=data
209+
)
210+
211+
self.assertEqual(resp.status_code, 200)
212+
self.assertFormError(
213+
response=resp,
214+
form="form",
215+
field="code",
216+
errors="验证码错误"
217+
)

accounts/urls.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
"""
1515

1616
from django.conf.urls import url
17-
from django.contrib.auth import views as auth_view
1817
from django.urls import path
19-
from . import views
18+
2019
from .forms import LoginForm
20+
from . import views
2121

2222
app_name = "accounts"
2323

@@ -33,4 +33,11 @@
3333
name='logout'),
3434
path(r'account/result.html',
3535
views.account_result,
36-
name='result')]
36+
name='result'),
37+
url(r'^forget_password/$',
38+
views.ForgetPasswordView.as_view(),
39+
name='forget_password'),
40+
url(r'^forget_password_code/$',
41+
views.ForgetPasswordEmailCode.as_view(),
42+
name='forget_password_code'),
43+
]

accounts/utils.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import typing
2+
from datetime import timedelta
3+
4+
from django.core.cache import cache
5+
6+
from DjangoBlog.utils import send_email
7+
8+
_code_ttl = timedelta(minutes=5)
9+
10+
11+
def send_verify_email(to_mail: str, code: str, subject: str = "邮件验证码"):
12+
"""发送重设密码验证码
13+
Args:
14+
to_mail: 接受邮箱
15+
subject: 邮件主题
16+
code: 验证码
17+
"""
18+
html_content = f"您正在重设密码,验证码为:{code}, 5分钟内有效,请妥善保管"
19+
send_email([to_mail], subject, html_content)
20+
21+
22+
def verify(email: str, code: str) -> typing.Optional[str]:
23+
"""验证code是否有效
24+
Args:
25+
email: 请求邮箱
26+
code: 验证码
27+
Return:
28+
如果有错误就返回错误str
29+
Node:
30+
这里的错误处理不太合理,应该采用raise抛出
31+
否测调用方也需要对error进行处理
32+
"""
33+
cache_code = get_code(email)
34+
if cache_code != code:
35+
return "验证码错误"
36+
37+
38+
def set_code(email: str, code: str):
39+
"""设置code"""
40+
cache.set(email, code, _code_ttl.seconds)
41+
42+
43+
def get_code(email: str) -> typing.Optional[str]:
44+
"""获取code"""
45+
return cache.get(email)

0 commit comments

Comments
 (0)