Sample code of user module and authority system of Django

  • 2021-07-26 08:09:06
  • OfStack

1 Introduction

Designing a good user system is often not so easy. The user system provided by Django can quickly realize basic functions, and can continue to expand on this basis to meet our needs.

Let's take a look at what functions are provided by Django's user system:

Provide user module (User Model) Permission verification (add, delete and modify permissions of existing modules by default) User Groups and Group Permissions Features User authentication and login function Some functions and decoration methods related to user login verification

If the user system is configured with Django, only several functions provided by Django can be called to realize the functions of user login, logout and authority verification. For example, the following scenario

Step 1 Log in


# some_view.py
from django.contrib.auth import authenticate, login
 
def login(request):
  username = request.POST['username']
  password = request.POST['password']
 
  # Django Offered authenticate Function to verify that the user name and password match in the database 
  user = authenticate(username=username, password=password)
 
  if user is not None:
    # Django Offered login Function to save the current logged-in user information to the session key Medium 
    login(request, user)
 
    #  Perform successful login operation, redirect to somewhere, etc. 
    ...
  else:
    #  Returns username and password error messages 
    ...

2. Write-off


# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout Function clears the information saved by the current user in the session 
  logout(request)

3. Verify login


# some_view.py
 
def some_fuction(request):
  user = request.user
  if user.is_authenticated:
    #  Logged-in user, you can go down 
  else:
    #  Returns required login information 

4. Verify that you have permissions


# some_view.py
 
def some_fuction(request):
  user = request.user
  if user.has_perm('myapp.change_bar'):
    #  Have permission, you can go down 
  else:
    #  Returns information such as forbidden access 

2 user modules

The user module class of Django is defined in auth application. If you want to use the user class of Django directly, add one line of django. contrib. auth to INSTALLAPP in setting configuration file.

This allows you to create users directly in your code using the User class:


from django.contrib.auth.models import User
user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
user.last_name = 'Lennon'
user.save()

Look at the source code for Django. What attributes are defined by the User class:


class User(AbstractUser):
  """
  Users within the Django authentication system are represented by this
  model.
  Username, password and email are required. Other fields are optional.
  """
  class Meta(AbstractUser.Meta):
    swappable = 'AUTH_USER_MODEL'

..., nothing?

Note that the User class inherits the AbstractUser class, and the user name and password information are defined in the parent class. So look at the definition of AbstractUser class again, and only one part is listed in space. The source code is https://github.com/django/django/blob/master/django/contrib/auth/models.py


class AbstractUser(AbstractBaseUser, PermissionsMixin):
  username = models.CharField(
    _('username'),
    max_length=150,
    unique=True,
    help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
    validators=[username_validator],
    error_messages={
      'unique': _("A user with that username already exists."),
    },
  )
  first_name = models.CharField(_('first name'), max_length=30, blank=True)
  last_name = models.CharField(_('last name'), max_length=30, blank=True)
  email = models.EmailField(_('email address'), blank=True)
  date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
....

You can see that the field username is defined in the AbstractUser class, and there are also email, first_name, last_name and so on. AbstractUser class also inherits AbstractBaseUser and PermissionsMixin class, AbstractBaseUser class provides password field and related methods for encrypting and saving passwords, and PermissionsMixin class provides User permission authentication function.

In general, an User class defines the following fields:

username: User name password: Password first_name: Name last_name: Name email: Mailbox groups: Group class many-to-many relational object manager user_permissions: Permission class many-to-many relational object manager is_staff: Staff or not is_active: Activated is_superuser: Administrator last_login: Last logon time date_joined: Registration time

In addition, to generate an User instance, you can use the following attributes:

is_authenticated: Read-only, used to determine whether the user exists. Only the AnonymousUser class this attribute is False. is_anonymous: Is used to distinguish between User and AnonymousUser objects.

The User class provides a number of methods to facilitate us to perform various operations:

set_password (raw_password): You can convert password to hash value, remember to call user. save () again to save. check_password (raw_password): Instead, compare the native password with the hash value password stored in the database (direct comparison is definitely different). has_perm (perm, obj=None), has_perms (perm_list, obj=None): Verify that the user has a permission or permission list get_all_permissions (obj=None): Returns all permissions that the user has

3 Authenticate and login users

Verifying the user name and password looks simple, just compare the submitted user name and password with the 1 saved in the database.

Such as:


# some_view.py
def some_fuction(request):
  username = request.POST['username']
  password = request.POST['password']
  try:
    user = User.objects.get(username=username, password=password)
  except ObjectNotExists:
    #  User name and password do not match 1 Users 
 
  ...

Because the password has been saved after hash, the above code must first submit the password value first hash and then search in the database, so it is necessary to write more than 1 code. These Django's have been taken into account, and an authenticate function is provided to verify the existence of the user, just like the example code in the introduction:


# some_view.py
from django.contrib.auth import authenticate, login
 
def login(request):
  username = request.POST['username']
  password = request.POST['password']
  user = authenticate(username=username, password=password)
  if user is not None:
    login(request, user)
    ...
  else:
    ...

Here, the authenticate and login functions under 1 are emphasized.

1.authenticate(**credentials)

The parameters passed in to be verified are username and password by default, which will call every authentication backend in the system configuration for verification, and return one User instance after verification, otherwise return None.

Every authentication backend refers to the authentication backend. The AUTHENTICATION_BACKENDS variable configured in setting. py defaults to ['django. contrib. auth. backends. ModelBackend']. You can add a custom authentication backend or use a third party. The authenticate function calls each one in sequence for authentication. If the first one fails, it uses the second for authentication and does not return None until all authentication backends fail.

See how this ModelBackend class returns an authenticated user (again shrinking the source code):


class ModelBackend(object):
  def authenticate(self, request, username=None, password=None, **kwargs):
    if username is None:
      username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
      user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:
 
      UserModel().set_password(password)
    else:
      if user.check_password(password) and self.user_can_authenticate(user):
        return user
# ...

Actually, the authenticate function calls the authenticate method of every authentication backend class. The ModelBackend class is slightly different from our previous verification method. It first takes one User instance corresponding to username from the database user table, then verifies whether password is correct through check_password method of User, and returns this User instance.

2.login(request, user, backend=None)

After receiving the HttpRequest object and an User object, the login function will save the current user information to the session cookie, so to use all the functions of the Django user system, you must also install the default session APP of Django.

Look at what the login function does:


# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout Function clears the information saved by the current user in the session 
  logout(request)
0

The login function saves the current user's 1-only identification information in request. session's SESSION_KEY, and the currently logged-in user object can be obtained from the session cookie at the next request.

If the login user is required to access the corresponding View, you can write this:


# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout Function clears the information saved by the current user in the session 
  logout(request)
1

The user information can be saved in the session through login function. Before the next request arrives at view, the session middleware of Django takes out the user object from the session cookie and assigns it to request. user. In this way, the logged-in user request. user. is_authenticated has a value of True and can perform corresponding operations. The unlogged user request. user returns an AnonymousUser object whose is_authenticated property value is always False, so redirect the request to the login page.

These two lines of code can be implemented by adding a decorator to the view function, such as


# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout Function clears the information saved by the current user in the session 
  logout(request)
2

The decorator login_required (redirect_field_name= 'next', login_url=None) can pass in the login_url parameter to set the address of the unlogged user's request for redirection, otherwise redirect to settings.LOGIN_URL.

4 Authority Authentication

The User model has two many-to-many fields: groups and user_permissions, which are related to the Pemission model. User and Pemission, User and Permission, Group and Permission are all many-to-many relationships, and Permission defines specific permissions with the following fields:


class Permission(models.Model):
  name = models.CharField(_('name'), max_length=255)   #  Permission name (used as display) 
  content_type = models.ForeignKey(    #  Content type:   Each model corresponds to 1 Content types for locating the specified model 
    ContentType,
    models.CASCADE,
    verbose_name=_('content type'),
  )
  codename = models.CharField(_('codename'), max_length=100)  #  The code name of the permission, used in the has_permission Function parameter 

Adding and deleting permissions to one user is simple: myuser. user_permissions. set ([permission_list]) myuser. user_permissions. add (permission, permission,...) myuser. user_permissions. remove (permission, permission,...) myuser. user_permissions. clear ()

Where permission is a concrete permission object. You can also add permissions through the user's group: group. permissions. set ([permission_list]) group. permissions. add (permission, permission,...) group. permissions. remove (permission, permission,...) group. permissions. clear ()

As long as this user joins the group, he also has group rights. The get_all_permissions (obj=None) method of the User object can obtain a list of all permissions (character list) for the user, or a corresponding group permission list can be obtained through get_group_permissions (obj=None).

What we do more often is to verify that the user has the specified permissions before performing operations in view, using the has_perm (perm, obj=None) method of the User object or the has_perms (perm_list, obj=None) judgment.

In the has_perm (perm, obj=None) method, perm is a permission string in the format "." If this permission is available, the method returns True. Similarly, in the ` has_perms (perm_list, obj=None) ` method, perm_list is a list of permission strings in the format "."

With the login_required decorator, the authority authentication also has a corresponding permission_required decorator:


# some_view.py
from django.contrib.auth import logout
 
def logout(request):
  # logout Function clears the information saved by the current user in the session 
  logout(request)
4

If raise_exception=True, an PermissionDenied exception is thrown and the default 403 page is returned.


Related articles: