采用Django实现的注册登录功能
主要实现内容
效果演示
未登录情况下,无法访问需要登录认证的页面
点击右上角的“气泡”按钮,未登录情况下,自动跳转到登录页面
自动跳转到登录页
注册演示
填写正确邮箱号后,邮箱将接受到激活信息,点击激活信息,即可登录
登录成功演示
点击右上角气泡按钮,跳转到气泡页
项目实现
安装所需库
pip install pillow
pip install django-simple-captcha
pip install django-simpleui
pip install six
项目目录结构
项目名为loginproject,应用名为login
定义模型
- login/models.py
from django.db import models
# Create your models here.
class User(models.Model):
gender=(
('male','男'),
('female','女'),
)
name=models.CharField(max_length=128,unique=True)
password=models.CharField(max_length=256)
email=models.EmailField(unique=True)
sex=models.CharField(max_length=32,choices=gender,default="男")
c_time=models.DateTimeField(auto_now_add=True)
#新增了has_confirmed字段,这是个布尔值,默认为False,也就是未进行邮件注册;
has_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
ordering=["-c_time"]
verbose_name="用户"
verbose_name_plural="用户"
class ConfirmString(models.Model):
'''
ConfirmString模型保存了用户和注册码之间的关系,一对一的形式;
'''
#code字段是哈希后的注册码;
code = models.CharField(max_length=256)
user = models.OneToOneField('User', on_delete=models.CASCADE, )
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.name + ": " + self.code
class Meta:
ordering = ["-c_time"]
verbose_name = "确认码"
verbose_name_plural = "确认码"
配置settings.py文件
配置INSTALLED_APPS
INSTALLED_APPS = [
'login', # 应用名
'simpleui', # 后台美化
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'captcha', # 图形验证码
]
配置静态文件static路径
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static","static"),
]
配置模板文件路径
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'static','templates')], # 添加模板文件路径
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'builtins':['django.templatetags.static'], # 添加该行,不用在静态模板中,手动添加{% load static %}
},
},
]
配置邮箱SMTP设置
可自行百度如何开启163邮箱的POP3/SMTP服务
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '填入自己的163邮箱'
EMAIL_HOST_PASSWORD = '开启163邮箱POP3/SMTP服务后的授权密码'
# 注册有效期天数
CONFIRM_DAYS = 7
注册模型字段到后台admin
- login/admin.py
from django.contrib import admin
from .models import *
# Register your models here.
class UserAdmin(admin.ModelAdmin):
list_display = ('name','sex','email','password','c_time')
list_filter = ['sex','name','c_time']
search_fields = ['sex','name','c_time']
class ConfirmAdmin(admin.ModelAdmin):
list_display = ('code','user','c_time')
admin.site.register(User,UserAdmin)
admin.site.register(ConfirmString,ConfirmAdmin)
配置form表单
在应用下创建forms.py文件
- login/forms.py
from django import forms
from captcha.fields import CaptchaField
class UserForm(forms.Form):
username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
captcha = CaptchaField(label='验证码')
class RegisterForm(forms.Form):
gender = (
('male', "男"),
('female', "女"),
)
username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
password1 = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
password2 = forms.CharField(label="确认密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
sex = forms.ChoiceField(label='性别', choices=gender)
captcha = CaptchaField(label='验证码')
数据库迁移
python manage.py makemigrations
python manage.py migrate
配置路由
设计应用路由
在应用下创建urls.py文件
- login/urls.py
from django.conf.urls import url
from .views import *
# CBV模式
urlpatterns = [
url(r'^index/', IndexView.as_view(),name='index'),
url(r'^login/', LoginView.as_view(),name='login'),
url(r'^register/', RegisterView.as_view(),name='register'),
url(r'^logout/', LogoutView.as_view(),name='logout'),
url(r'^confirm/$', UserConfirmView.as_view(),name='confirm'),
url(r'^content/', ContentView.as_view(),name='content'),
]
配置根路由
与项目名同名的目录下的urls.py文件
- loginproject/urls.py
from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'',include('login.urls')), # 配置app,login的url
url(r'^captcha', include('captcha.urls')), # 配置图形验证码路由
]
视图代码
- login/views.py
import hashlib
import datetime
from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from django import forms
from login import forms
from django.conf import settings
from django.views.generic import View
from functools import wraps
from django.utils.decorators import method_decorator
from django.core.mail import EmailMultiAlternatives
# Create your views here.
def hash_code(s, salt='mysite'): # 加盐
# 加密
h = hashlib.sha256()
s += salt
h.update(s.encode()) # update方法只接收bytes类型
return h.hexdigest()
def make_confirm_string(user):
# 激活确定
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
code = hash_code(user.name, now)
models.ConfirmString.objects.create(code=code, user=user,)
return code
def send_email(email, code):
# 邮箱发送
subject = '来自Mr Wolf的注册确认邮件'
text_content = '''感谢注册Mr Wolf,专注于Django实战的博客分享!\
如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
html_content = '''
<p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>Mr Wolf系统</a>,\
专注于Django实战的博客分享!</p>
<p>请点击站点链接完成注册确认!</p>
<p>此链接有效期为{}天!</p>
'''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)
msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
msg.attach_alternative(html_content, "text/html")
msg.send()
# session认证装饰器
def login_check(func):
@wraps(func)
def wrapper(request):
if not request.session.get('is_login', None):
return redirect("/login/")
else:
return func(request)
return wrapper
# CBV模式
class IndexView(View):
'''
首页
'''
def get(self,request):
return render(request, 'login/index.html')
class RegisterView(View):
'''
注册
'''
def get(self,request):
register_form = forms.RegisterForm(request.POST)
return render(request, 'login/register.html', {'message': '','register_form':register_form})
def post(self,request):
register_form = forms.RegisterForm(request.POST)
if register_form.is_valid(): # 获取数据
username = request.POST.get('username','')
password1 = request.POST.get('password1','')
password2 = request.POST.get('password2','')
email = request.POST.get('email','')
sex = request.POST.get('sex','')
if password1 != password2: # 判断两次密码是否相同
message = "两次输入的密码不同!"
return render(request, 'login/register.html', {'message': message, 'register_form': register_form})
else:
same_name_user = models.User.objects.filter(name=username)
same_email_user = models.User.objects.filter(email=email)
if same_name_user: # 用户名唯一
message = '用户已经存在,请重新选择用户名!'
return render(request, 'login/register.html', {'message': message, 'register_form': register_form})
elif same_email_user: # 邮箱地址唯一
message = '该邮箱地址已被注册,请使用别的邮箱!'
return render(request, 'login/register.html', {'message': message, 'register_form': register_form})
else:
# 当一切都OK的情况下,创建新用户
new_user = models.User()
new_user.name = username
new_user.password = hash_code(password1) # 使用哈希加密密码
new_user.email = email
new_user.sex = sex
new_user.save()
code = make_confirm_string(new_user)
send_email(email, code)
message = '请前往注册邮箱,进行邮件确认!'
return render(request, 'login/confirm.html', {'message': message}) # 跳转到等待邮件确认页面。
class LoginView(View):
'''
登录
'''
def get(self,request):
login_form = forms.UserForm(request.POST)
return render(request,'login/login.html',{'login_form':login_form})
def post(self,request):
login_form = forms.UserForm(request.POST)
message = "请检查填写的内容!"
if login_form.is_valid():
username = request.POST.get('username','')
password = request.POST.get('password','')
try:
user = models.User.objects.get(name=username)
if not user.has_confirmed:
message = "该用户还未通过邮件确认!"
return render(request, 'login/login.html', {'message': message,'login_form':login_form})
if user.password == hash_code(password): # 哈希值和数据库内的值进行比对
# 记录会话状态
request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name
return redirect('/index/')
else:
message = "密码不正确!"
except:
message = "用户不存在!"
return render(request, 'login/login.html', {'message': message,'login_form':login_form})
class LogoutView(View):
'''
退出
'''
def get(self,request):
if not request.session.get('is_login', None):
return redirect("/index/")
request.session.flush()
# 或者使用下面的方法
# del request.session['is_login']
# del request.session['user_id']
# del request.session['user_name']
return redirect("/index/")
class UserConfirmView(View):
'''
邮箱激活认证
'''
def get(self,request):
code = request.GET.get('code', None)
try:
confirm = models.ConfirmString.objects.get(code=code)
except:
message = '无效的确认请求!'
return render(request, 'login/confirm.html', {'message': message})
c_time = confirm.c_time
now = datetime.datetime.now()
if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
confirm.user.delete()
message = '您的邮件已经过期!请重新注册!'
return render(request, 'login/confirm.html', {'message': message})
else:
confirm.user.has_confirmed = True
confirm.user.save()
confirm.delete()
message = '感谢确认,请使用账户登录!'
login_form = forms.UserForm(request.POST)
return render(request, 'login/login.html', {'message': message, 'login_form': login_form})
class ContentView(View):
# 气泡页面视图,登录后才允许访问
@method_decorator(login_check)
def get(self,request):
message = '验证气泡显示'
return render(request, 'content.html', {'message': message})
前端实现
到GitHub上下载参考