django 用户注册/登录与发送激活邮件

992 阅读4分钟

完成登录页面

构建前端页面

其中比较主要的是前端的一个 form 类型的注册框:

			<div class="reg_form clearfix">
				<form method="post" action="/user/register/">
					{% csrf_token %}
				<ul>
					<li>
						<label>用户名:</label>
						<input type="text" name="user_name" id="user_name">
						<span class="error_tip">提示信息</span>
					</li>					
					<li>
						<label>密码:</label>
						<input type="password" name="pwd" id="pwd">
						<span class="error_tip">提示信息</span>
					</li>
					<li>
						<label>确认密码:</label>
						<input type="password" name="cpwd" id="cpwd">
						<span class="error_tip">提示信息</span>
					</li>
					<li>
						<label>邮箱:</label>
						<input type="text" name="email" id="email">
						<span class="error_tip">提示信息</span>
					</li>
					<li class="agreement">
						<input type="checkbox" name="allow" id="allow" checked="checked">
						<label>同意”天天生鲜用户使用协议“</label>
						<span class="error_tip2">提示信息</span>
					</li>
					<li class="reg_sub">
						<input type="submit" value="注 册" name="">
					</li>
				</ul>				
				</form>
				{{ errmsg }}
			</div>

注册路由

在 apps.user.urls 中注册路由:

from django.conf.urls import url

from apps.user.views import RegisterView

urlpatterns = [
    url(r"^register/$", RegisterView.as_view(), name='register'),  # 将注册改为类视图的使用模式

]

编写视图

首先完成页面视图,即我们请求注册页面时发出 get 请求的视图: 在 apps.user.views.py 中使用类视图完成:

from django.shortcuts import render
from django.views import View


class RegisterView(View):
    """类视图"""
    def get(self, request):
        return render(request, "register.html")

这一步完成之后,我们请求 http://127.0.0.1:8000/user/register/ 即可访问到注册页。 访问注册页

接着我们来完成用户提交操作对应的视图:

import re

from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from apps.user.models import User


class RegisterView(View):
    """类视图"""
    def get(self, request):
        return render(request, "register.html")

    def post(self, request):
        return register_handle(request)


def register_handle(request):
    user_name = request.POST.get("user_name")
    password = request.POST.get("pwd")
    email = request.POST.get("email")
    if not all([user_name, password, email]):
        return render(request, "register.html", {"errmsg": '数据不完整'})
    try:
        is_exist = User.objects.get(username=user_name)
    except User.DoesNotExist:  # 在查询不到的时候抛出异常
        is_exist = None
    if is_exist:
        return render(request, "register.html", {"errmsg": '该用户名已存在'})

    if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
        return render(request, "register.html", {"errmsg": '邮箱格式不正确'})

    if not request.POST.get("allow") == "on":
        return render(request, "register.html", {"errmsg": '请先同意用户协议'})

    user = User.objects.create_user(user_name, email, password)
    user.is_active = 0
    user.save()
    # TODO 发送激活邮件
    return HttpResponse("注册成功 请到邮箱激活登录")

在页面完成注册,成功响应后查看数据库,可以看到数据库中已经出现了这个注册的用户。 发起注册的 post 请求 数据库新增注册用户

只不过用户的状态是未激活: is_active = 0 ,我们通过下一小节中的发送邮件 --> 用户点击激活链接 --> 校验后更该用户状态来激活我们的注册用户。

发送邮件

在 django 中发送邮件,首先我们需要去邮箱服务中申请自己的一个服务授权密码。 这部分的内容可以参考以前的文章: blog.csdn.net/Enjolras_fu…

blog.csdn.net/Enjolras_fu…

在这里不再赘述。

首先我们来新增一些邮件相关的配置:

# 邮件相关的配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '15626046299@163.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = EMAIL_HOST_PASSWORD
# 收件人看到的发件人
EMAIL_FROM = 'mydailyfresh<15626046299@163.com>'   # 注意尖括号内的邮箱必须和上面的邮箱地址一致 否则发送不出去

其中, EMAIL_HOST_PASSWORD 说我的客户端服务授权码,在这里应该将其换成自己的授权码,同时更改为自己的邮箱地址。

接着,我们在 apps.user.views 中林临时创建一个函数来封装发送邮件的过程:

def my_send_mail(msg, user_email):
    '''
    msg: 需要发送的信息
    user_email: 用户填写的接收激活信息的邮箱
    '''
    send_mail("dailyfrsh 用户注册",
              '',
              settings.EMAIL_FROM,
              [user_email],
              html_message=msg
              )

我们在终端模拟一次邮件的发送过程: 在终端模拟发送邮件的过程 查看邮箱,可以看到刚刚测试发送的内容: 收到测试邮件

注册时发送激活邮件逻辑

安装 itsdangerous

pip install itsdangerous

这个库是用来生成激活 token 的。 我们来思考一下使用 itsdangerous 的逻辑: 用户注册请求请求发送成功之后,我们在数据库中生成一个未激活用户。然后我们将用户的 id 加密后发送给用户的注册邮箱,用户从自己的邮箱中点击注册链接,后端拿到链接参数解密后将相应 id 的用户激活。 需要注意的几个点:1 是加密和解密的秘钥是不能泄露的;2 是加密应该具有时效性。 我们来进行一次脚本测试: 加密过程

解密过程

现在我们就可以将这段逻辑加入 用户的注册视图中了 :

	# ... 
    # TODO 发送激活邮件
    ser = Serializer(settings.SECRET_KEY, 3600)  # 参数分别是秘钥以及以秒为单位的过期时间
    user_info = {"confirm": user.id}
    ret = ser.dumps(user_info)
    token = ret.decode("utf-8")
    active_link = "http://127.0.0.1:8000/user/active?token={}".format(token)
    my_send_mail(active_link, user.email) 
    # ... 

位置在注册用户创建之后: 发送激活邮件

完成激活逻辑视图

最后我们还要增加最后一个视图,用来接收用户点击了激活链接后的逻辑处理。 这一步基本的功能就是解密、修改用户用户信息了。

# apps.user.views.py
class ActiveView(View):
    """用户激活视图"""
    def get(self, request):
        token = request.GET.get("token")
        token = token.encode("utf-8")
        ser = Serializer(settings.SECRET_KEY, 3600)
        try:
            user_info = ser.loads(token)
        except SignatureExpired:
            return HttpResponse("激活链接已超时")
        except:
            err_msg = traceback.format_exc()
            return HttpResponse("激活失效: {}".format(err_msg))
        else:
            # 激活成功
            user_id = user_info.get("confirm")
            user = User.objects.get(id=user_id)
            user.is_active = 1
            user.save()
            # 跳转到登录页面
            return HttpResponse("登录页面")
            # TODO 跳转到真正的登录页面
            # return redirect(reverse("user:login"))

# apps.user.urls.py 
urlpatterns = [
	# ... 

    url(r"^active/$", ActiveView.as_view(), name='active'),
    
    # ... 

]

注册用户

注册成功响应

到邮箱中点击激活链接

点击激活链接进入模拟的登录页面

登录

django 内置了用户登录登出,以及状态校验的一些方法,使完成用户的登录登出操作十分方便。

照例我们首先来完成登录的前端页面: 给出主体部分的 form 的 html 代码:

				<div class="form_input">
<!--					不设置表单的 action 属性时,提交表单时 会向浏览器当前所在地址提交请求 -->
					<form method="post">
						{% csrf_token %}
						<input type="text" name="username" class="name_input" value="{{ username }}" placeholder="请输入用户名">
						<div class="user_error">输入错误</div>
						<input type="password" name="pwd" class="pass_input" placeholder="请输入密码">
						<div class="pwd_error">输入错误</div>
						<div class="more_input clearfix">
							<input type="checkbox" name="remember">
							<label>记住用户名</label>
							<a href="#">忘记密码</a>
						</div>
						<input type="submit" name="" value="登录" class="input_submit">
					</form>
				</div>

增加路由:

# apps.user.urls.py 
urlpatterns = [
	# ... 

    url(r"^login/$", LoginView.as_view(), name='login'),  # 用户登录
    url(r"^logout/$", LogoutView.as_view(), name='logout'),  # 用户登出

]

对应添加两个视图 LoginView 和 LogoutView。 登录后的下一步逻辑是跳转到首页,我们暂时模拟替代。

class LoginView(View):
    """用户登录视图"""
    def get(self, request):
        if "username" in request.COOKIES:
            user_name = request.COOKIES.get("username")
        else:
            user_name = ''
        return render(request, "login.html", {"username": user_name})

    def post(self, request):
        user_name = request.POST.get("username")
        pass_word = request.POST.get("pwd")

        if not all([user_name, pass_word]):
            return render(request, 'login.html', {"errmsg": "数据不完整"})

        user = authenticate(username=user_name, password=pass_word)
        if not user:
            return render(request, 'login.html', {"errmsg": "用户不存在或者密码错误"})
        elif not user.is_active:
            return render(request, 'login.html', {"errmsg": "用户尚未激活"})

        login(request, user)
        response = HttpResponse("登录成功 ")
        is_remembered = request.POST.get("remember")
        if is_remembered:
            response.set_cookie("username", user.username, max_age=7*24*3600)
        else:
            response.delete_cookie('username')
        return response


class LogoutView(View):
    """用户登录视图"""
    def get(self, request):
        logout(request)
        return HttpResponse("模拟首页")

这里需要注意的几个问题, response.set_cookie 、delete_cookie 以及 get_cookie 方法是与前端交互的,将 cookie 信息存储在浏览器中。当下一次浏览器发起请求的时候,可以从 request.COOKIES 中获取。

参考

www.bilibili.com/video/av417…