django源码分析之权限系统_擒贼先擒王(代码片段)

dtstack dtstack     2023-02-02     567

关键词:

乍见

Django内置的权限系统已经很完善了,加上django-guardian提供的功能,基本上能满足大部分的权限需求。暂且不说django-guardian,我们先来看下Django内置的权限系统:django.contrib.auth 包。
技术分享图片

相识

一般权限系统分为全局权限和对象权限。Django只提供了一个对象权限的框架,具体实现由第三方库django-gardian完成。我们只看全局权限。

先来看auth包暴露出哪些接口。

django.contrib.auth.__init__.py

def load_backend(path):
    return import_string(path)()


def _get_backends(return_tuples=False):
    backends = []
    for backend_path in settings.AUTHENTICATION_BACKENDS:
        backend = load_backend(backend_path)
        backends.append((backend, backend_path) if return_tuples else backend)
    if not backends:
        raise ImproperlyConfigured(
            ‘No authentication backends have been defined. Does ‘
            ‘AUTHENTICATION_BACKENDS contain anything?‘
        )
    return backends


def get_backends():
    return _get_backends(return_tuples=False)

前三个方法都是为了加载backends。一个backend其实就是一个class,必须实现authenticate和get_user两个方法。每当我们这样验证用户时

authenticate(username=‘username‘, password=‘password‘)

django就会去调用这些backend class,用其提供的方法去验证用户权限。那django是如何知道要调用哪些backend class呢?答案就在settings.py中,默认为

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

那Django是如何调用这些backend class的呢?

def authenticate(**credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend, backend_path in _get_backends(return_tuples=True):
        try:
            inspect.getcallargs(backend.authenticate, **credentials)
        except TypeError:
            # This backend doesn‘t accept these credentials as arguments. Try the next one.
            continue

        try:
            user = backend.authenticate(**credentials)
        except PermissionDenied:
            # This backend says to stop in our tracks - this user should not be allowed in at all.
            return None
        if user is None:
            continue
        # Annotate the user object with the path of the backend.
        user.backend = backend_path
        return user

    # The credentials supplied are invalid to all backends, fire signal
    user_login_failed.send(sender=__name__,
            credentials=_clean_credentials(credentials))

由此可见,Django会在第一个验证正确的backend class调用完成后停止,或者碰到PermissionDenied异常也会停止,所以backend class的顺序也很重要。可以添加自定义的backend class。

def login(request, user):
    """
    Persist a user id and a backend in the request. This way a user doesn‘t
    have to reauthenticate on every request. Note that data set during
    the anonymous session is retained when the user logs in.
    """
    session_auth_hash = ‘‘
    if user is None:
        user = request.user
    if hasattr(user, ‘get_session_auth_hash‘):
        session_auth_hash = user.get_session_auth_hash()

    if SESSION_KEY in request.session:
        if _get_user_session_key(request) != user.pk or (
                session_auth_hash and
                request.session.get(HASH_SESSION_KEY) != session_auth_hash):
            # To avoid reusing another user‘s session, create a new, empty
            # session if the existing session corresponds to a different
            # authenticated user.
            request.session.flush()
    else:
        request.session.cycle_key()
    request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
    request.session[BACKEND_SESSION_KEY] = user.backend
    request.session[HASH_SESSION_KEY] = session_auth_hash
    if hasattr(request, ‘user‘):
        request.user = user
    rotate_token(request)
    user_logged_in.send(sender=user.__class__, request=request, user=user)

login方法,顾名思义,登录用户,同时设置好session,最后发送登入成功通知

def logout(request):
    """
    Removes the authenticated user‘s ID from the request and flushes their
    session data.
    """
    # Dispatch the signal before the user is logged out so the receivers have a
    # chance to find out *who* logged out.
    user = getattr(request, ‘user‘, None)
    if hasattr(user, ‘is_authenticated‘) and not user.is_authenticated():
        user = None
    user_logged_out.send(sender=user.__class__, request=request, user=user)

    # remember language choice saved to session
    language = request.session.get(LANGUAGE_SESSION_KEY)

    request.session.flush()

    if language is not None:
        request.session[LANGUAGE_SESSION_KEY] = language

    if hasattr(request, ‘user‘):
        from django.contrib.auth.models import AnonymousUser
        request.user = AnonymousUser()

相对的,logout方法,负责登出用户,清理session,最后设置当前用户为匿名用户

def get_user_model():
    """
    Returns the User model that is active in this project.
    """
    try:
        return django_apps.get_model(settings.AUTH_USER_MODEL)
    except ValueError:
        raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form ‘app_label.model_name‘")
    except LookupError:
        raise ImproperlyConfigured(
            "AUTH_USER_MODEL refers to model ‘%s‘ that has not been installed" % settings.AUTH_USER_MODEL
        )

Django不推荐直接使用User class,而是通知get_user_model方法获取当前的用户class(或者使用settins.AUTH_USER_MODEL)。这是为了防止因为开发者使用了自定义用户class而导致的信息错误。

def update_session_auth_hash(request, user):
    """
    Updating a user‘s password logs out all sessions for the user if
    django.contrib.auth.middleware.SessionAuthenticationMiddleware is enabled.

    This function takes the current request and the updated user object from
    which the new session hash will be derived and updates the session hash
    appropriately to prevent a password change from logging out the session
    from which the password was changed.
    """
    if hasattr(user, ‘get_session_auth_hash‘) and request.user == user:
        request.session[HASH_SESSION_KEY] = user.get_session_auth_hash()

最后这个方法的使用场景很少。一般我们更新用户密码时,会在session中清除用户登录信息,导致用户需要重新登录。而使用update_session_auth_hash我们就可以在更新用户密码的同时更新用户的session信息,这样,用户就不需要重新登录了。

回想

擒贼先擒王,以上都是django.contrib.auth包中的__init__.py入口文件中的内容,背后还有很多“能工巧匠”,否则怎么支撑起auth整套权限系统?后续文章会一一介绍。


django源码分析之server(代码片段)

乍见Django内置的server基本包括两部分:django.core.servers和django.core.handlers相识servers.basehttp是Django自身提供的一个用于开发测试的server模块,其中提供的WSGIServer、ServerHandler、WSGIRequestHandler其实都是属于WSGIserver,django只不过是对pytho... 查看详情

django(63)drf权限源码分析与自定义权限(代码片段)

前言上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析源码入口:APIView.py文件下的initial方法下的check_permissionsdefcheck_permissions(self,request):"""检查是否应... 查看详情

django——session源码分析(代码片段)

首先我们导入django.contrib.sessions.middleware这个中间件,查看里面的Session源码fromdjango.contrib.sessions.middlewareimportSessionMiddleware我们可以看到一个类,可以把他分为3部分:classSessionMiddleware(MiddlewareMixin):def__init__(self,ge 查看详情

openharmony移植案例:buildlite源码分析之hb命令__entry__.py(代码片段)

...要:本文介绍了buildlite轻量级编译构建系统hb命令的源码,主要分析了_\\entry__.py文件。本文分享自华为云社区《移植案例与原理-buildlite源码分析之hb命令__entry__.py》,作者:zhushy。hb命令可以通过pythonpip包管理器... 查看详情

django内置权限系统源码解读

前言之前有篇文章​​Django自定义认证系统原理及源码分析解读​​带大家分析解读了Django的认证逻辑,而且我们也知道认证是基础,认证通过之后,用户登录到系统,能看到那些,能操作那些,这些都是有​​权限控制​​的... 查看详情

django权限之二级菜单(代码片段)

遗漏知识点1.构建表结构时,谁被关联谁就是主表,在层级删除的时候,删除子表的时候,主表不会被删除,反之删除主表的话,字表也会被删除,使用related_name=None  反向查询,起名用的之前使用表名小写+__set.all() 使用related_name... 查看详情

flask系列之源码分析(代码片段)

...术点python之__setattr__python之threading.localpython之偏函数flask源码上下文管理1、综述过程将请求对象压入栈1.请求进入__call__---> wsgi_app---> ctx=self.request_context(environ)初始化请求对象2.通过ctx.puth()建立2个请求堆栈(采用thread 查看详情

django之权限组件(代码片段)

一、需求分析RBAC(Role-BasedAccessControl,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,... 查看详情

springsecurity认证源码分析之账户权限

当我进一步用springsecurity,首先就有下面三个问题让我很疑惑:1、springsecurity到底是在哪个环节验证用户权限的?2、为什么代码实现层没有直接校验权限的地方?过滤器假定写了一个过滤器,继承了OncePerRequestFilter:publicclassJwtAu... 查看详情

django---加载installed_apps的源码分析(代码片段)

运行django项目,我们除了可以通过django图形界面启动,我们也可以通过命令行方式启动,启动方式如下:pythonmanage.pyrunserver当我们创建django项目时候,会生成如下目录mysite/├──manage.py#管理文件└──mysite#... 查看详情

django框架之rbac+contenttype(代码片段)

...张表二、content_type表RBAC(基于角色的权限访问控制),在django框架中已经帮我们实现好了。一、基于角色的权限访问控制的六张表二、content_type表#给Django中的所有模块中的所有表进行编号存储到content_type表中#应用一:权限表的... 查看详情

django基于pycharm开发之四[关于静态文件的使用,配置以及源码分析](原创)(代码片段)

对于django静态文件的使用,如果开发过netcore程序的开发人员,可能会比较容易理解django关于静态文件访问的设计原理,个人觉得,这是一个middlerware的设计,但是在django中我们在配置中看到,他其实并不是放在middleware中配置的... 查看详情

django之权限管理插件(代码片段)

参考:https://www.cnblogs.com/alex3714/articles/6661911.html   http://www.cnblogs.com/wupeiqi/articles/6229414.html1.     什么是权限?权限就是对软件系统中各种资源的访问和操作的控制!2.     查看详情

openstack之horizon源码分析

一、基础准备:  Horizon是基于djangowebframework开发的标准的Python wsgi程序,django的设计专注于代码的高度可重用,信奉DRY原则,一切面向对象,而Horizon可以说高度match了django的设计风格。网站程序基本有三部分组成,业务逻... 查看详情

srs之hls部署实例源码分析(代码片段)

1.综述SRS关于HLS的具体配置可见:HLS部署实例SRS关于hls的配置文件内容如下:listen1935;max_connections1000;daemonoff;srs_log_tankconsole;vhost__defaultVhost__hlsenabledon;hls_fragment10;hls_window60;hls_path./objs/nginx/html;hls_ 查看详情

django(64)频率认证源码分析与自定义频率认证(代码片段)

前言有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定 频率认证源码分析defcheck_throttles(self,request):"""检查是否应限制请求。如果请求受到限制,则引发适当的... 查看详情

ios底层探索之block——block的探索和源码分析(代码片段)

Block的本质是什么吗?__Block底层又做了什么呢?在上一篇博客中,已经探索到block的本质是结构体(__main_block_impl_0)继承自__block_impl,block可以捕获外部变量,通过__block修饰内部可以变更外部变量的值。那么本篇... 查看详情

《v8源码分析》(代码片段)

...;https://blog.csdn.net/counsellor/category_9549440.html (4条消息)V8源码分析之d8源码注解(第五篇)_counsellor的专栏-CSDN博客_v8源码分析之d80x00前言没了你,我颓废了自己。心里那些苦,都只哽在喉咙里,一想起来就泪如雨下。----王国维0x01... 查看详情