Django搭建网络相册:登录与登出

318 views, 2021/08/10 updated   Go to Comments

作为个人相册来说,通常只需要简单的用户管理就足够了,原因如下:

  • 具有操作资源权限的只有自己,不用考虑诸多网络安全的问题。
  • Django 后台提供了丰富的管理能力。
  • 评论等需要登录参与的功能可以托管给第三方。(比如 Github 的某些评论插件)

话虽如此,但前台登录退出功能还是要有的,后台总归没那么直观。

因此本章就来简单实现下管理员账户在前台的登入和登出。

后端代码

浏览器发起的 HTTP 请求,总共定义了八种请求类型,来表示对指定资源的不同操作方式。比如说前面章节在访问相册主页时,实际上提起的是 GET 请求。GET 请求被称为安全请求,因为按照规范,它不应该在服务器上产生任何结果,也就是不会修改信息。

安全方法也被称作是幂等的

其他常用的还有 POST 请求,它的使用又分两种情况。

一种情况是 POST 请求会修改服务器资源,比如发表新文章、修改用户数据等,另一种情况是请求会携带敏感信息。用户的登入登出就属于第二种情况。

GET 请求会将信息暴露在地址栏中,且更容易受到跨域等攻击。

因此首页的 home() 视图函数不仅要处理显示图片的 GET 请求,还要肩负用户登录管理的 POST 请求。

像这样修改 views.py 文件:

# /photo/views.py

from django.shortcuts import render
from photo.models import Photo
from django.contrib.auth import authenticate, login, logout

def home(request):
    photos = Photo.objects.all()
    context = {'photos': photos}

    # 处理登入登出的POST请求
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user     = authenticate(request, username=username, password=password)
        # 登入
        if user is not None and user.is_superuser:
            login(request, user)
        # 登出
        isLogout  = request.POST.get('isLogout')
        if isLogout == 'True':
            logout(request)

    return render(request, 'photo/list.html', context)
  • 请求体 request 包含用户请求的相关信息。request.method 即表明了请求的类型。如果是登录相关的 POST 请求,那么进行下一步的处理。
  • request.POST.get() 可获取到 POST 请求中包含的数据。这里就是用户名和密码了。
  • 验证用户名、密码是否正确要用 authenticate() 方法。切记不能直接去比较模型中的字符串,因为密码是加密存储的。
  • 如果此用户存在并且是管理员,那么则 login() 函数登入。否则不进行处理。
  • 请求中还包含一个标志位 isLogout 用来判断用户是想登入还是登出。如果它为真,则 logout() 退出账号。

除此之外其他还是和之前一样了。

接下来是前端部分。

前端代码

登录功能一般会将入口放在页眉上,这样可以保证用户可以在任意网页位置进行登入登出操作。

因此修改 header.html 模板文件,变成下面这样:

<!-- /templates/header.html -->

<!-- 导航栏 -->
<nav class="navbar navbar-expand navbar-dark bg-dark">
    <div class="container">
        <!-- 标志 -->
        <a 
            class="navbar-brand" 
            href="#"
            data-bs-toggle="modal" 
            data-bs-target="#login"
        >
            <h2>
                Awesome Album
            </h2>
        </a>
        <!-- 登录模态窗 -->
        {% if user.is_superuser %}
            <ul class="navbar-nav">
                <li class="nav-item">
                <h2>
                    <a class="nav-link active" aria-current="page" href="#">
                        +
                    </a>
                </h2>
                </li>
            </ul>
        {% endif %}
    </div>
</nav>

<!-- 登出 -->
{% if user.is_superuser %}

<div class="modal fade" id="login">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body">
                <p>确认退出账号吗?</p>
                <form action="{% url 'home' %}" method="post">{% csrf_token %}
                    <input type="text" id="isLogout" name="isLogout" value="True" hidden>
                    <button type="submit" class="btn btn-danger">Logout</button>
                </form>
            </div>
        </div>
    </div>
</div>

<!-- 登录 -->
{% else %}

<div class="modal fade" id="login">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body">
                <form action="{% url 'home' %}" method="post">{% csrf_token %}
                    <div class="mb-3">
                        <label for="username" class="form-label">Username</label>
                        <input type="text" class="form-control" id="username" name="username">
                    </div>
                    <div class="mb-3">
                        <label for="pwd" class="form-label">Password</label>
                        <input type="password" class="form-control" id="pwd" name="password">
                        <div id="passwordHelp" class="form-text">
                            We'll never share your password with anyone else.
                        </div>
                    </div>
                    <button type="submit" class="btn btn-primary">Login</button>
                </form>
            </div>
        </div>
    </div>
</div>

{% endif %}

主要变化就是将导航栏里的网站标题变成了模态窗的入口。页面会根据用户的登录与否,决定显示登入还是登出的模态窗,并同时显示或隐藏“创建新相片”的按钮。

需要注意的点:

  • {% if user.is_superuser %} 判断当前是否是管理员用户,与视图中的 user.is_superuser 是相同的。在模板中要包裹在标签内。
  • 根据是否是管理员用户,模态窗的具体内容有所不同。这个内容就决定了用户是要登入还是登出。
  • 表单元素 <form ...> 中的 action="{% url 'home' %}" 说明此表单提交给当前页面。注意这里的 'home' ,就是当初我们在 path() 中给 url 赋予的名字。method="post" 说明表单提交的是 POST 请求。
  • 登出的模态窗有一个 <input ... hidden> 的隐藏数据,此数据不显示给用户,但是会悄悄提交 isLogout="True" 这个数据。就是靠它来确定登入登出状态的。
  • 注意每个表单都有 {% csrf_token %} 这么个东西,这是 Django 用来防止跨域攻击的小插件,默认情况下表单必须携带,否则会报 403 错误。

接下来测试。

测试

刷新页面,点击页眉中的标题:

提示登录的模态窗就显示出来了。

输入管理员账号密码后,点击提交按钮,有如下变化:

注意看页眉右边出现个加号,这就表示登录成功了。

这个加号用于后续章节上传图片。目前暂时没有功能。

再次点击页眉标题,此时显示的就是提示退出登录的模态窗了。点击红色的提交按钮后,应该就正常退出了账户,右侧的小加号也消失了。

总结

本章学习了 Django 中如何处理用户的登录问题的。

你会发现后端的 Python 代码总是比前端 html 代码更少,很大一部分原因是 Django 很好的封装了通用的功能。用户验证就是个例子,你可以在后台中看看用户密码,它是以密文的形式存储的。但是你的代码中完全没管加密与解密的问题,直接调用内置的 authenticate() 函数就搞定了。

登入登出功能也一样,Django 封装了具体的处理方式,你调用 login()logout() 函数即可。

另一方面,你真的不用害怕大量的 html 代码。教程中涉及的内容大概就和 Word 文档排版的难度差不多,唯一区别就是 Word 排版是用鼠标点点点,而 html 将点点点的动作转化成了英文字母。多查阅一定看得懂。

下一章聊聊如何批量上传图片。

点赞 or 吐槽?评论区告诉我!




本文作者: 杜赛
发布时间: 2021年08月10日 - 13:02
最后更新: 2021年08月10日 - 13:02
转载请保留原文链接及作者
本站使用 Github 评论后端。鉴于国内复杂的网络环境,如遇评论无法加载、发表等问题,请尝试刷新页面,或更换上网姿势。