Django Combat 1-реализация функции управления разрешениями-03: аутентификация пользователя

Python GitHub Django PyCharm

1 Дизайн модели управления правами

1.1 Создать приложение

Создайте новое приложение с именем system, которое включает основные системные модули, такие как управление пользователями, управление меню и управление полномочиями.

  • Используйте pycharm, чтобы открыть наш проект, щелкните правой кнопкой мыши корневой каталог проекта, выберите «Создать» → «Пакет Python» и введите приложения во всплывающем окне.Этот пакет используется для хранения всех приложений, созданных в проекте.
  • Выберите «Инструменты» над pycharm и нажмите «Выполнить».manage.pyЗадача ..., затем окно откроется ниже Pycharm, введите STARTAPP System и нажмите Enter, чтобы создать приложение, как показано ниже:

image

  • Переместите только что созданную систему в приложения
  • Чтобы успешно получить доступ к нашему новому приложению, щелкните правой кнопкой мыши приложения и выберите «Отметить каталог как» → «Корень источников».
  • Измените sandboxMP/sandboxMP/settings.py и добавьте следующее:
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

1.2 Создайте модель аутентификации разрешений

В проекте sandboxMP используется настраиваемая модель аутентификации авторизации.Описание модели:

Menu: 菜单管理,用来存储系统可用的URL
Role: 角色组,通过外键关联Menu,角色组中的用户将继承Role关联菜单的访问权限
Structure:组织架构,包含单位和部门信息
UserProfile: 自定义用户认证模型,替换系统原有的User模型

Следующее содержимое представляет собой подробное содержимое модели аутентификации авторизации. Скопируйте следующее содержимое в apps/system/models.py.

from django.db import models
from django.contrib.auth.models import AbstractUser


class Menu(models.Model):
    """
    菜单
    """
    name = models.CharField(max_length=30, unique=True, verbose_name="菜单名")  # unique=True, 这个字段在表中必须有唯一值.
    parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父菜单")
    icon = models.CharField(max_length=50, null=True, blank=True, verbose_name="图标")
    code = models.CharField(max_length=50, null=True, blank=True, verbose_name="编码")
    url = models.CharField(max_length=128, unique=True, null=True, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '菜单'
        verbose_name_plural = verbose_name

    @classmethod
    def get_menu_by_request_url(cls, url):
        return dict(menu=Menu.objects.get(url=url))


class Role(models.Model):
    """
    角色:用于权限绑定
    """
    name = models.CharField(max_length=32, unique=True, verbose_name="角色")
    permissions = models.ManyToManyField("menu", blank=True, verbose_name="URL授权")
    desc = models.CharField(max_length=50, blank=True, null=True, verbose_name="描述")


class Structure(models.Model):
    """
    组织架构
    """
    type_choices = (("unit", "单位"), ("department", "部门"))
    name = models.CharField(max_length=60, verbose_name="名称")
    type = models.CharField(max_length=20, choices=type_choices, default="department", verbose_name="类型")
    parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父类架构")

    class Meta:
        verbose_name = "组织架构"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class UserProfile(AbstractUser):
    name = models.CharField(max_length=20, default="", verbose_name="姓名")
    birthday = models.DateField(null=True, blank=True, verbose_name="出生日期")
    gender = models.CharField(max_length=10, choices=(("male", "男"), ("female", "女")),
                              default="male", verbose_name="性别")
    mobile = models.CharField(max_length=11, default="", verbose_name="手机号码")
    email = models.EmailField(max_length=50, verbose_name="邮箱")
    image = models.ImageField(upload_to="image/%Y/%m", default="image/default.jpg",
                              max_length=100, null=True, blank=True)
    department = models.ForeignKey("Structure", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="部门")
    post = models.CharField(max_length=50, null=True, blank=True, verbose_name="职位")
    superior = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="上级主管")
    roles = models.ManyToManyField("role", verbose_name="角色", blank=True)

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name
        ordering = ['id']

    def __str__(self):
        return self.name

1.3 Использование модели

После определения моделей нам также нужно указать Django использовать эти модели.Нам нужно изменить файл settings.py и добавить имя приложения, где models.py находится в INSTALLED_APPS:

INSTALLED_APPS = [
     ...原内容省略...
     'system',
]

Чтобы использовать пользовательскую модель аутентификации UserProfile, вам также необходимо добавить следующее в settings.py:

AUTH_USER_MODEL = 'system.UserProfile'

Уведомление:
Тип поля ImageField используется при определении пользовательской модели.Перед выполнением makemigrations необходимо установить пакет зависимостей: подушка, открыть окно CMD, войти в виртуальную среду python этого проекта, а затем установить подушку:

C:\Users\RobbieHan>workon sandboxMP
(sandboxMP) C:\Users\RobbieHan>pip install pillow

Вы также можете выполнить команду установки в окне терминала pycharm: pip install Pillow

Наконец, выполните makemigrations и выполните миграцию, чтобы сгенерировать таблицу данных, используйте инструменты pycharm, нажмите «Выполнить».manage.pyЗадача ..., в управлении окном введите следующую команду:

makemigrations
migrate

1.4 Знания, связанные с моделями

Тип поля:

Типы полей, используемые в модели аутентификации авторизации, следующие:

CharField: 用来存储字符串,必须制定一个参数 max_length用来限定字段最大长度
Foreignkey: 是一个关联字段,创建多表之间的多对一关系,如果创建同表之间的递归关联关系,可以使用models.ForeignKey('self')
ManyToManyField: 用来实现多对多的关联关系
DateField: 日期时间字段
EmailField: email字段,用来检查email地址是否合法
ImageField: 图片字段,用来定义图片上传和图片检查,需要安装pillow库

Опции поля:

unique: 设置为True, 则表示这个字段必须有唯一值,这是从数据库级别来强制数据唯一,后面我们还会介绍通过form验证来确保数据输入的唯一
verbose_name:
blank: 默认值是False, 设置为True,则该字段润许为空
null: 默认值是False,如果为True,Django会在数据库中将空值转存为NULL
choices: 是一个可迭代结构(元祖),每个元组中的第一个元素,是存储在数据库中的值;第二个元素是使人容易理解的描述。

on_delete : До django 2.0 параметр on_delete не был необходим при определении поля ассоциации. После django 2.0 параметр on_delete должен быть определен при определении поля ассоциации. Общие параметры следующие:

on_delete=models.CASCADE,     # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING,  # 删除关联数据,什么也不做
on_delete=models.PROTECT,     # 删除关联数据,引发错误ProtectedError
on_delete=models.SET_NULL,    # 删除关联数据,与之关联的值设置为null
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值

Следует отметить, что при использовании set_null поле необходимо устанавливать при определении модели, например:

user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)

Также при использовании SET_DEFAULT вам необходимо предварительно определить значение по умолчанию:

user = models.ForeignKey(User, on_delete=models.SET_DEFAULT, default='默认值')

Для получения дополнительных типов полей и параметров полей см.:
docs.Django project.com/en/1.11/hotwind…

2 Аутентификация пользователя и ограничение доступа

Требования для аутентификации пользователя при входе в систему следующие:

  • Пользователи могут получить доступ к определенным страницам только после входа в систему.
  • Если пользователь не вошел в систему, для прямого доступа произойдет переход к экрану входа в систему,
  • После того, как пользователь завершит вход в интерфейс входа в систему с переходом, он автоматически получит доступ и перейдет к ранее доступному адресу.
  • Пользователь может использовать имя пользователя, номер мобильного телефона или другие поля в качестве имени пользователя для входа.

В pycharm выберите sandboxMP/apps/system, щелкните правой кнопкой мыши, выберите «Создать» → «Файл Python», введите имя во всплывающем окне: views_user и импортируйте необходимые модули на только что созданную страницу:

from django.shortcuts import render
from django.views.generic.base import View
from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from django.urls import reverse

Описание: Представления, созданные ниже, записываются в файл sandboxMP/apps/system/views_user.py.

2.1 Создание представления индексной страницы

Просмотр страницы индекса является первым представлением, созданным этим проектом:

class IndexView(View):

    def get(self, request):
        return render(request, 'index.html')

Введение в очки знаний:

1. Просмотр:Введение «представлений» в официальной документации Django используется для инкапсуляции логики обработки пользовательских запросов и возврата ответов.
Мы можем определить функции представления, чтобы принимать веб-запросы и возвращать веб-ответы, или мы можем использовать объекты представлений на основе классов Реализации представлений в этом проекте — это все представления, созданные на основе классов, которые отличаются от представлений на основе функций.

  • Код, связанный с HTTP-методом (GET, POST и т. д.), может быть написан в отдельном методе, и нет необходимости судить о HTTP-методе через условные переходы.
  • Код может быть разложен на повторно используемые компоненты, такие как Mixin (множественное наследование), с использованием преимуществ объектно-ориентированной технологии, более гибкого использования и простоты расширения.

2. функция визуализации:Быстрая функция Django, которая объединяет заданный шаблон с заданным словарем контекста и возвращает необязательный объект HttpRespose, синтаксис: render(request, template_name, context=None, content_type=None, status=None, using=None), где request и template_name обязательные параметры, а другие являются необязательными параметрами.

2.2 Создайте представление входа пользователя

Прежде чем создавать представление для входа пользователя, сначала создайте файл sandboxMP/apps/system/forms.py для проверки ввода пользователя для входа в систему, содержимое которого выглядит следующим образом:

from django import forms


class LoginForm(forms.Form):
    username = forms.CharField(required=True, error_messages={"requeired": "请填写用户名"})
    password = forms.CharField(required=True, error_messages={"requeired": "请填写密码"})

Создайте представление входа пользователя:

from .forms import LoginForm


class LoginView(View):

    def get(self, request):
        if not request.user.is_authenticated:
            return render(request, 'system/users/login.html')
        else:
            return HttpResponseRedirect('/')

    def post(self, request):
        redirect_to = request.GET.get('next', '/')
        login_form = LoginForm(request.POST)
        ret = dict(login_form=login_form)
        if login_form.is_valid():
            user_name = request.POST['username']
            pass_word = request.POST['password']
            user = authenticate(username=user_name, password=pass_word)
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(redirect_to)
                else:
                    ret['msg'] = '用户未激活!'
            else:
                ret['msg'] = '用户名或密码错误!'
        else:
            ret['msg'] = '用户和密码不能为空!'
        return render(request, 'system/users/login.html', ret)

Введение в очки знаний:
Django использует сеансы и промежуточное программное обеспечение для перехвата объектов запроса в системе аутентификации. Они предоставляют свойство request.user по каждому запросу, представляющим текущий пользователь. Если текущий пользователь не вошел в систему, это свойство будет установлено на экземпляр Anonymousizer, в противном случае он будет экземпляром пользователя.
1. request.user.is_authentential:Используется для определения того, вошел ли пользователь в систему, например, в LoginView:

 # 当用户访问登陆页面时,判断用户如果未登入则访问登陆页面,如果登入则跳转到首页
if not request.user.is_authenticated:
    return render(request, 'system/users/login.html')
else:
    return HttpResponseRedirect('/')

2. is_valid():Для проверки поля используется метод силы формы.Когда значение входного поля является допустимым, он возвращает True и сохраняет данные формы в атрибуте clean_data.
3. аутентификация (запрос = нет, ** учетные данные):Используется для аутентификации пользователей. Учетные данные – это параметр ключевого слова. По умолчанию – имя пользователя и пароль. Если проверено средство аутентификации, возвращается объект User.
4. Войти (запрос, пользователь, Backend = None):Для входа пользователя из представления, а идентификатор пользователя хранится в таблице сеансов. Примечание. Перед вызовом функции login() необходимо использовать аутентификацию(), чтобы успешно аутентифицировать пользователя.
5. HttpResponseRedirect[источник]:Используется для перенаправления доступа. Параметр представляет собой адрес перенаправления, который может быть полным URL-адресом или абсолютным путем к проекту.

2.3 Создайте представление выхода пользователя

class LogoutView(View):

    def get(self, request):
        logout(request)
        return HttpResponseRedirect(reverse('login'))

Введение в очки знаний:
1. выход (запрос):Выйдите из системы.
2. реверс (имя вида):Выполните обратный анализ URL-адреса в соответствии с именем URL-адреса.

2.4 Настройка маршрутизации URL-адресов пользователей

Если вы хотите получить доступ к приложению представления через URL-адрес, вам также необходимо настроить маршрутизацию URL-адресов и изменить файл sandboxMP/sandboxMP/urls.py:

from django.contrib import admin
from django.urls import path

from system.views_user import IndexView, LoginView, LogoutView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='index'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

2.5 Создайте аутентифицированного пользователя

Выберите «Инструменты» в pycharm и нажмите «Выполнить».manage.pyЗадача..., в открывшемся окне введите createsuperuser, следуйте подсказкам ввести имя пользователя, электронную почту и пароль, операция следующая:

manage.py@sandboxMP > createsuperuser
"C:\Program Files\JetBrains\PyCharm2017.3.2\bin\runnerw.exe" C:\Users\RobbieHan\Envs\sandboxMP\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm2017.3.2\helpers\pycharm\django_manage.py" createsuperuser D:/ProjectFile/sandboxMP
用户名:  admin
邮箱:  robbie_han@outlook.com
Warning: Password input may be echoed.
Password:  !qaz@wsx
Warning: Password input may be echoed.
Password (again):  !qaz@wsx
Superuser created successfully.

Запустите проект для доступа к системе:http://127.0.0.1:8000, мы не вошли в систему пользователя и можем напрямую получить доступ к домашней странице, что не соответствует нашим требованиям. Затем внедрите ограничения доступа к странице, требующие, чтобы пользователи вошли в систему для доступа.

2.6 Ограничения доступа к странице

Требования к реализации ограничений доступа к странице:

  • Пользователи должны войти в систему, чтобы получить доступ к определенным страницам
  • Если пользователь не вошел в систему и получает прямой доступ, он перейдет к интерфейсу входа в систему.
  • После того, как пользователь завершит логин на прыжковой посадочной странице, пользователь автоматически доступа к адресу доступа до прыжка

Создайте новый файл sandboxMP/apps/system/mixin.py и напишите следующее содержимое:

from django.contrib.auth.decorators import login_required


class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **init_kwargs):
        view = super(LoginRequiredMixin, cls).as_view(**init_kwargs)
        return login_required(view)

Измените sandboxMP/sandboxMP/settings.py, добавьте LOGIN_URL

LOGIN_URL = '/login/'

Представления, к которым должны получить доступ вошедшие в систему пользователи, должны наследовать только LoginRequiredMixin.Измененное представление IndexView выглядит следующим образом:

from .mixin import LoginRequiredMixin

class IndexView(LoginRequiredMixin, View): 

    def get(self, request):
        return render(request, 'index.html')

Примечание. LoginRequiredMixin находится в крайнем левом углу списка наследования.

Перезапускаем проект, снова заходим на домашнюю страницу, открываем браузер, вводим http://127.0.0.1:8000, после чего обнаружим, что URL в браузере станет таким:http://127.0.0.1:8000/логин/?следующий=/,Нам нужно перейти на домашнюю страницу после того, как вы выйдете в интернет.
Используйте наших пользователей, созданных в разделе 2.5: Admin, пароль: !QAZ@WSX посадочная система

2.7 Доступ к медиафайлам

Хотя аватар по умолчанию устанавливается при создании пользователя, и изображение, используемое аватаром по умолчанию, размещено, аватар не может отображаться после входа пользователя в систему, поэтому необходимо настроить доступ к медиафайлам.

image

Медиафайл — это файл, загруженный пользователем, и путь к нему изменен, например файл аватара, загруженный пользователем.
Установить каталог для загрузки файлов

Измените файл sandboxMP/sandboxMP/settings.py и добавьте следующую конфигурацию:

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Откройте sandboxMP/sandboxMP/urls.py и добавьте следующую конфигурацию:

from django.conf import settings
from django.urls import re_path
from django.views.static import serve

if settings.DEBUG:
    urlpatterns += [
        re_path(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
    
    ]

Вы можете увидеть аватар пользователя, обновив страницу.Следует отметить, что причина использования if settings.DEBUG здесь заключается в том, что этот режим конфигурации следует использовать только в режиме разработки, и эти медиафайлы должны обрабатываться через веб-интерфейс. в производственной среде Доступ.

Самые последние и наиболее полные документы публикуются на Планете знаний, вы можете выполнить поиск в общедоступной учетной записи «Планета знаний» через WeChat и ответить непосредственно на «52824366», чтобы получить доступ.
Документация в этом разделе соответствует версии исходного кода: GitHub.com/Робби Хан/ это…

Заинтересованные друзья очень рады быть гостем на моем Github или Nuggets, В свободное время ставьте лайк или звезду, а также дарите розы в руки и оставляйте аромат
Документ, подтверждающий адрес проекта:GitHub.com/Робби Хан/ это…
Адрес проекта с открытым исходным кодом облегченной системы управления офисом:GitHub.com/Робби Хан/…