Django的身份认证系统

 

1 . 在Django中使用身份认证系统

Django的身份认证系统实际上是一个app,该app叫做django.contrib.auth,它在django contrib模块下

使用时只需要在setting.py文件的INSTALLED_APPS中添加:'django.contrib.auth' 即可

身份认证系统包括以下内容:

  • 用户
  • 权限:标志指定用户是否可以执行某项任务。
  • 组:向多个用户应用标签和权限的通用方法。
  • 可配置的密码散列系统
  • 用于登录用户或限制内容的表单和视图工具
  • 可插拔的后端系统

该身份认证系统包含密码强度检查、登陆限制、第三方身份验证、对象级权限设置

 

2. 添加与Django身份认证系统相关联的app

注意:使用身份认证系统时,一定要添加django.contrib.contenttypes这个app

这个app的叫做内容类型系统,它将允许身份认证系统与我们创建的模型相关联

也就是说身份认证系统与我们创建的模型需要通过内容类型系统进行处理

 

3. 添加与身份认证系统相关联的中间件

添加完这两个app后,还要在中间件中添加相关内容,即在setting.py文件的MIDDLEWARE中添加以下内容:

  • SessionMiddleware:session中间件,用来管理请求会话
  • AuthenticationMiddleware:身份验证中间件,用来使用户和请求相关联

总的来说身份认证系统使用了2个app,2个中间件

然后执行python manage.py migrate命令,数据库就会创建权限、用户、组相关的表结构

此时,我们可以执行 python manage.py createsuperuser 命令创建超级用户,然后启动django项目

访问:http://127.0.0.1:8000/admin/ 这个网址,对用户进行管理

注意:后台管理模块依赖于 django.contrib.admin 这个app

 

4. 身份认证系统中的密码加密

django默认使用PBKDF2进行密码存储,该密码机制非常安全,如果想要使用其他密码机制,则要在setting文件中修改即可,

以下是django提供的密码机制:

[
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
    'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
    'django.contrib.auth.hashers.CryptPasswordHasher',
]

对应的python算法是:

pbkdf2_sha256
pbkdf2_sha1
argon2
bcrypt_sha256
bcrypt
sha1
md5
unsalted_sha1
unsalted_md5
crypt

下面的代码是修改默认的密码机制,首先要在setting.py中添加下面的内容,

然后修改PASSWORD_HASHERS数组中下标为【0】的的数据,下标为0的指的就是密码保存时的加密算法:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

 将上述的PBKDF2修改为MD5

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

这里要说明一下DJango加密数据后的存储规则,其中$是分隔符:

<algorithm>$<iterations>$<salt>$<hash>
  • 第一个成员是散列算法,指的是md5,pbkdf2之类
  • 第二个成员是迭代次数(工作因子),跟君算法不同可以是字符串或数字
  • 第三个成员是随机盐
  • 第四个是密码哈希

两个不同的算法生成的结果如下,前两条是用pbkdf2加密的,后两条是md5:

启动Django,使用admin后台管理系统,添加用户,就能查看该用户的加密后的密码

Django会按照你在setting中的配置生成指定规则的密码,上面已说过

 

当然,django的密码加密不仅于此,还能将旧系统中的老密码重新进行加密升级,这个升级过程称之为包装

即将已有用户的以sha1加密的密码包装升级为PBKDF2加密的密码,这个升级过程和用户的登录无关

这一块用的人较少,仅说明一下,想看具体内容请访问官方文档:Django官方文档

 

 5. 身份认证系统中的用户密码修改

密码修改有两种,一种是不需要原密码进行修改,一种是需要原密码进行修改

不需要原密码进行修改有下面三种方式:

  • admin后台管理系统
  • python manage.py changepassword 修改
  • 通过自定义视图修改

 admin修改如下图:

 命令行修改方式如下:

 在Views.py视图中修改方式如下:

def mysql(request):
    template = loader.get_template('app_Mysql/table.html')
    context = {
        'latest_question_list': {'xxx': 123},
    }
    from django.contrib.auth.models import User
    u = User.objects.get(username='zhangsan')
    u.set_password('zhangsan66666')
    u.save()

需要身份验证修改密码的方式如下:

Django的身份验证除了可以使用自带的身份验证,也可以使用自定义的身份验证

下面是使用自带的身份验证:

def mysql(request):
    template = loader.get_template('app_Mysql/table.html')
    context = {
        'latest_question_list': {'xxx': 123},
    }
    # from django.contrib.auth.models import User
    # u = User.objects.get(username='zhangsan')
    # u.set_password('zhangsan66666')
    # u.save()

    from django.contrib.auth import authenticate
    user = authenticate(username='zhangsan', password='zhangsan66666')
    if user is not None:
        print('账户、密码输入正确')
    else:
        print('账户、密码输入不正确')
    return HttpResponse(template.render(context,request))

authenticate会根据获得的用户密码,去【检查】【配置的】【身份认证后端】,并返回一个是否有效的User用户(,假如配置了多个认证后端的话,检查过程会挨个检查认证后端,)

如果无效则返回None,有效则返回该用户

这里说明一下什么是身份认证后端:

身份认证后端:用于与【用户模型Model】相关联的【存储用户名和密码】的【一组服务】,称之为验证后端

即这个后端和用户模型挂钩,且为这个用户模型提供了相关验证服务

既然是Django提供的服务,那么我们就可以对这个服务进行定制、修改、替换

例如:公司已经存在一个LDAP,可以为每个员工存储用户名和密码,如果用户在LDAP和基于Django的应用程序中有单独的帐户,那么网络管理员和用户自己都会很麻烦。(用户登录时django会先验证该用户,然后再去ladp验证)

上面的情况就需要修改Django默认的认证后端,首先要在setting.py中添加下面的内容:

AUTHENTICATION_BACKENDS =[
    'django.contrib.auth.backends.ModelBackend'
]

然后对其进行替换,替换为我们自已定义的身份验证类。

也可以在这个数组中加入我们自定义的身份认证后端,加入后,程序会按照下面的方式进行,

先到默认的ModelBackend验证,然后再到我们加入的验证后端中验证,知道即可,不再赘述。

默认的验证类包含了用户身份验证、用户组、用户权限、权限组等,还是很全的

from __future__ import unicode_literals

from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission

UserModel = get_user_model()


class ModelBackend(object):
    """
    Authenticates against settings.AUTH_USER_MODEL.
    """

    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:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a non-existing user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def user_can_authenticate(self, user):
        """
        Reject users with is_active=False. Custom user models that don't have
        that attribute are allowed.
        """
        is_active = getattr(user, 'is_active', None)
        return is_active or is_active is None

    def _get_user_permissions(self, user_obj):
        return user_obj.user_permissions.all()

    def _get_group_permissions(self, user_obj):
        user_groups_field = get_user_model()._meta.get_field('groups')
        user_groups_query = 'group__%s' % user_groups_field.related_query_name()
        return Permission.objects.filter(**{user_groups_query: user_obj})

    def _get_permissions(self, user_obj, obj, from_name):
        """
        Returns the permissions of `user_obj` from `from_name`. `from_name` can
        be either "group" or "user" to return permissions from
        `_get_group_permissions` or `_get_user_permissions` respectively.
        """
        if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
            return set()

        perm_cache_name = '_%s_perm_cache' % from_name
        if not hasattr(user_obj, perm_cache_name):
            if user_obj.is_superuser:
                perms = Permission.objects.all()
            else:
                perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj)
            perms = perms.values_list('content_type__app_label', 'codename').order_by()
            setattr(user_obj, perm_cache_name, set("%s.%s" % (ct, name) for ct, name in perms))
        return getattr(user_obj, perm_cache_name)

    def get_user_permissions(self, user_obj, obj=None):
        """
        Returns a set of permission strings the user `user_obj` has from their
        `user_permissions`.
        """
        return self._get_permissions(user_obj, obj, 'user')

    def get_group_permissions(self, user_obj, obj=None):
        """
        Returns a set of permission strings the user `user_obj` has from the
        groups they belong.
        """
        return self._get_permissions(user_obj, obj, 'group')

    def get_all_permissions(self, user_obj, obj=None):
        if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
            return set()
        if not hasattr(user_obj, '_perm_cache'):
            user_obj._perm_cache = self.get_user_permissions(user_obj)
            user_obj._perm_cache.update(self.get_group_permissions(user_obj))
        return user_obj._perm_cache

    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_active:
            return False
        return perm in self.get_all_permissions(user_obj, obj)

    def has_module_perms(self, user_obj, app_label):
        """
        Returns True if user_obj has any permissions in the given app_label.
        """
        if not user_obj.is_active:
            return False
        for perm in self.get_all_permissions(user_obj):
            if perm[:perm.index('.')] == app_label:
                return True
        return False

    def get_user(self, user_id):
        try:
            user = UserModel._default_manager.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None


class AllowAllUsersModelBackend(ModelBackend):
    def user_can_authenticate(self, user):
        return True


class RemoteUserBackend(ModelBackend):
    """
    This backend is to be used in conjunction with the ``RemoteUserMiddleware``
    found in the middleware module of this package, and is used when the server
    is handling authentication outside of Django.

    By default, the ``authenticate`` method creates ``User`` objects for
    usernames that don't already exist in the database.  Subclasses can disable
    this behavior by setting the ``create_unknown_user`` attribute to
    ``False``.
    """

    # Create a User object if not already in the database?
    create_unknown_user = True

    def authenticate(self, request, remote_user):
        """
        The username passed as ``remote_user`` is considered trusted.  This
        method simply returns the ``User`` object with the given username,
        creating a new ``User`` object if ``create_unknown_user`` is ``True``.

        Returns None if ``create_unknown_user`` is ``False`` and a ``User``
        object with the given username is not found in the database.
        """
        if not remote_user:
            return
        user = None
        username = self.clean_username(remote_user)

        # Note that this could be accomplished in one try-except clause, but
        # instead we use get_or_create when creating unknown users since it has
        # built-in safeguards for multiple threads.
        if self.create_unknown_user:
            user, created = UserModel._default_manager.get_or_create(**{
                UserModel.USERNAME_FIELD: username
            })
            if created:
                user = self.configure_user(user)
        else:
            try:
                user = UserModel._default_manager.get_by_natural_key(username)
            except UserModel.DoesNotExist:
                pass
        return user if self.user_can_authenticate(user) else None

    def clean_username(self, username):
        """
        Performs any cleaning on the "username" prior to using it to get or
        create the user object.  Returns the cleaned username.

        By default, returns the username unchanged.
        """
        return username

    def configure_user(self, user):
        """
        Configures a user after creation and returns the updated user.

        By default, returns the user unmodified.
        """
        return user


class AllowAllUsersRemoteUserBackend(RemoteUserBackend):
    def user_can_authenticate(self, user):
        return True

了解更相信的内容,看官方文档:官方文档---身份认证后端

6. 用户的权限缓存

Django默认的ModelBackend会在用户请求登陆时获取该用户的相应权限,然后进行缓存,该缓存直至用户会话结束,否则不会刷新。

如果在页面上对该用户的权限做出了修改并且要求该用户新修改的权限立即生效,那么就需要重新获取该用户的相关权限

下面是获取用户权限的方法例子:

from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404

from myapp.models import BlogPost

def user_gains_perms(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    # any permission check will cache the current set of permissions
    user.has_perm('myapp.change_blogpost')

    content_type = ContentType.objects.get_for_model(BlogPost)
    permission = Permission.objects.get(
        codename='change_blogpost',
        content_type=content_type,
    )
    user.user_permissions.add(permission)

    # Checking the cached permission set
    user.has_perm('myapp.change_blogpost')  # False

    # Request new instance of User
    # Be aware that user.refresh_from_db() won't clear the cache.
    user = get_object_or_404(User, pk=user_id)

    # Permission cache is repopulated from the database
    user.has_perm('myapp.change_blogpost')  # True

    ...

7. 用户退出操作

添加相应的url,然后添加对应的views中添加以下内容即可:

#创建自定义的View,用来封装返回结果使用
class ResponseResult_View(View):
    def __init__(self):
        self.code = 201
        self.message = '操作成功'
        self.data = None

    def get_result(self):
        return {'code': self.code, 'message': self.message, 'data': self.data}

# 用户退出
class User_logout(ResponseResult_View):
    @logging(level='INFO')
    def get(self, request):
        logout(request)
        return render(request, "app_User/login.html")

8. 未验证用户的重定向操作

重定向使用装饰器能够轻松解决指定视图用户访问时的验证问题

下面是django的默认装饰器login_required,该装饰器可以无参,也可以有参:

无参情况,在setting设置跳转:

#无参情况

#setting文件内容,需要指定LOGIN_URL
未登录,重定向页面
LOGIN_URL = '/user/login'

#主页
@login_required
def User_main(request):
    return render(request, 'app_User/main.html',)

有参情况,直接在装饰器参数中指定跳转:

@login_required(login_url='/user/login')
def User_main(request):
    return render(request, 'app_User/main.html',)

注意:user/login要配置在app的urls中,如下图:

from django.conf.urls import url

from app_User import views

urlpatterns = [

    url(r'^main$', views.User_main, name='main'),
    url(r'^login$', views.User_login.as_view(), name='login'),
    url(r'^logout$', views.User_logout.as_view(), name='logout'),

]

至于登录模块的验证的更多方法,请参考:官方文档authenticate

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值