zoukankan      html  css  js  c++  java
  • 【Python】django权限管理

    参考:http://www.cnblogs.com/esperyong/

    参考:https://docs.djangoproject.com/en/1.8/topics/auth/default/#topic-authorization

    在Django的世界中,在权限管理中有内置的Authentication系统。用来管理帐户,组,和许可。还有基于cookie的用户session。

    django中内置的权限控制1-User Model


    这篇blog主要用来探讨这套内置的Authentication系统。

    Django内置的权限系统包括以下三个部分:
    用户(Users)
    许可(Permissions):用来定义一个用户(user)是否能够做某项任务(task)
    组(Groups):一种可以批量分配许可到多个用户的通用方式

    首先需要在Django中安装这个组件:
    在settings.py配置好数据库连接,运行python manage.py syncdb 。这一步将生成管理界面使用的数据库表。
    将'django.contrib.auth'和'django.contrib.contenttypes'放到settings.py中的INSTALLED_APPS中(使用contenttypes的原因是auth中的Permission模型依赖于contenttypes)

    我们可以执行python manage.py shell来启动命令行,对其中的一些API进行学习和使用。

    >> User对象 <<

    首先最重要的开始就是User模型

    User模型对应于一个用户,一个帐户,位于'django.contrib.auth.models'模块中。
    User对象有两个多对多的属性分别是:groups和user_permissions

    >> 新建User对象 <<

    1 from django.contrib.auth.models import User
    2 u = User.objects.create_user('test1','test1','aaa')

    User对象的Manager,UserManager:
    和其他的模型一样,User模型类的objects属性也是一个Manager对象,但是User的Manager对象是自定义的,增加了一些方法:
    create_user(username,email=None,password=None)
    该方法创建保存一个is_active=True的User对象并返回。username不能够为空,否则抛出ValueError异常。email和password都是可选的。email的domain部分会被自动转变为小写。password如果没有提供,则User对象的set_unusable_password()方法将会被调用。
    make_random_password(length=10,allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
    该方法返回一个给定长度和允许字符集的密码。其中默认的allowed_chars有一些字符没有,比如i,l等等。

    >> User对象的属性 <<:

      username:字符串类型。必填。30个字符以内。
      first_name:字符串类型。可选。30个字符以内。
      last_name:字符串类型。可选。30个字符以内。
      email:可选。
      password:明文密码的hash或者是某种元数据。该属性不应该直接赋值明文密码,而应该通过set_password()方法进行赋值,在后面有详细说明TODO。
      is_staff:Boolean类型。用这个来判断是否用户可以登录进入admin site。
      is_active:Boolean类型。用来判断该用户是否是可用激活状态。在删除一个帐户的时候,可以选择将这个属性置为False,而不是真正删除。这样如果应用有外键引用到这个用户,外键就不会被破坏。
      is_superuser:Boolean类型。该属性用来表示该用户拥有所有的许可,而无需明确的赋予给他。
      last_login:datetime类型。最近一次登陆时间。
      date_joined:datetime类型。创建时间。

    可以在新建用户时直接使用这些属性:

    1 from django.contrib.auth.models import User
    2 u = User.objects.create_user('test1','test1','aaa')
    3 u.first_name='a'
    4 u.first_name='b'
    5 u.save()

    >> 修改User对象 <<:

    1 from django.contrib.auth.models import User
    2 u = User.objects.get(username__exact='test1')
    3 u.username='test2'
    4 u.save()

    注意修改完对象属性需要u.save()。

    千万不要直接给User的password属性赋值,应该用set_password()方法进行赋值。

    Tips:这些用户属性就是将数据库中用户授权信息的赋值动作。

    用python manage.py changepassword *username*来进行修改,需要输入两次密码。

    >> User对象方法 << 

    像set_password()一样,除了DjangoModel对象的通用方法之外,User对象有以下特有方法:
      is_anonymous():
        永远返回False.用来将User对象和AnonymousUser(未登录的匿名用户)对象作区分用的识别方法。通常,最好用is_authenticated()方法。
      is_authenticated():
        永远返回True。该方法不代表该用户有任何的许可,也不代表该用户是active的,而只是表明该用户提供了正确的username和password。
      get_full_name():
        返回一个字符串,是first_name和last_name中间加一个空格组成。
      set_password(raw_password):
        调用该方法时候传入一个明文密码,该方法会进行hash转换。该方法调用之后并不会保存User对象。
      check_password(raw_password):
        如果传入的明文密码是正确的返回True。该方法和set_password是一对,也会考虑hash转换。
      set_unusable_password():
        将用户设置为没有密码的状态。调用该方法后,check_password()方法将会永远返回false。但是如果,调用set_password()方法重新设置密码后,该方法将会失效,has_usable_password()也会返回True。
      has_usable_password():
        在调用set_unusable_password()方法之后,该方法返回False,正常情况下返回True。
      get_group_permissions(obj=None):
        返回该用户通过组所拥有的许可(字符串列表每一个代表一个许可)。obj如果指定,将会返回关于该对象的许可,而不是模型。
      get_all_permissions(obj=None):
        返回该用户所拥有的所有的许可,包括通过组的和通过用户赋予的许可。
      has_perm(perm,obj=None):
        如果用户有传入的perm,则返回True。perm可以是一个格式为:'<app label>.<permission codename>'的字符串。如果User对象为inactive,该方法永远返回False。和前面一样,如果传入obj,则判断该用户对于这个对象是否有这个许可。
      has_perms(perm_list,obj=None):
        和has_perm一样,不同的地方是第一个参数是一个perm列表,只有用户拥有传入的每一个perm,返回值才是True。
      has_module_perms(package_name):
        传入的是Django app label,按照'<app label>.<permission codename>'格式。当用户拥有该app label下面所有的perm时,返回值为True。如果用户为inactive,返回值永远为False。
      email_user(subject,message,from_email=None):
        发送一封邮件给这个用户,依靠的当然是该用户的email属性。如果from_email不提供的话,Django会使用settings中的DEFAULT_FROM_EMAIL发送。
      get_profile():
        返回一个和Site相关的profile对象,用来存储额外的用户信息。这个返回值会在另一片博文中详细描述。

    django中内置的权限控制2-Login Logout


    而在Web应用中,任何的权限系统要做的第一步就是用户识别,也就是我们常说的登陆(login)。只有正确的登陆校验,知道用户是谁了,才能够知道用户能干什么,那就是许可(Permission)需要负责解决的事情,而Group则是批量设置许可的时候的一个便利手段。

    >> 请求用户是否登陆的验证 <<

    django有一套方法,可以在每个view方法能够接收到的request对象中增加权限验证相关的方法。要做到这一点,首先需要:

    在settings文件中对MIDDLEWARE_CLASSES变量增加上述两个Middleware类SessionMiddleware和AuthenticationMiddleware。

    1 MIDDLEWARE_CLASSES = (
    2     'django.contrib.sessions.middleware.SessionMiddleware',
    3     'django.middleware.locale.LocaleMiddleware',
    4     'django.middleware.common.CommonMiddleware',
    5     'django.middleware.csrf.CsrfViewMiddleware',
    6     'django.contrib.auth.middleware.AuthenticationMiddleware',
    7     'django.contrib.messages.middleware.MessageMiddleware',
    8     'django.middleware.transaction.TransactionMiddleware',
    9 )

    在view中,我们就可以使用request.user获取当前的登陆用户User对象。如果当前用户没有登陆,那么request.user将会是我们之前所说的AnonymousUser对象。我们可以用User对象的is_authenticated()方法将这两者区分开来,我们可以使用django新建一个项目,在view中进行测试:

    1 def test(request):
    2     if request.user.is_authenticated():
    3         return render_to_response('test.html',{'User_status':'login'})
    4     else:
    5         return render_to_response('test.html',{'User_status':'no login'})

    在相应的视图函数前面增加@login_required修饰符可以实现非登录用户禁止访问:

    from django.contrib.auth.decorators import login_required
    
    @login_required
    def test(request):
        ...

    1、如果用户没登录, 重定向到/accounts/login/(settings.LOGIN_URL),并且把当前绝对URL作为next参数用get方法传递过去

    2、如果用户已登录, 正常地执行视图函数

    >>authenticate验证<<

    使用命令行可以进行测试,authenticate(username,password)函数需要两个参数username,password,如果校验通过则返回User对象,如果校验不通过返回None,例如:

    from django.contrib.auth import authenticate, login
     
    def my_view(request):
        user = authenticate(username='root', password='admin')
        if user is not None:
            print "User ok"
        else:
            print "password err or no user"

    >> login_required <<

    定义:django.contrib.auth.decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME,login_url=None])

    login_required方法接受两个参数:
    redirect_field_name:默认值是next。用来定义登陆成功之后的跳回之前访问界面的url。
    login_url:默认值是settings.LOGIN_URL。用来指定登陆界面的url。如果不传入改参数,就需要确保settings.LOGIN_URL的值是正确设置的。

    1 from django.contrib.auth.decorators import login_required
    2  
    3 @login_required(login_url='/admin/')
    4 def test(request):
    5     ...

    示例view:

    from django.http import HttpResponse,HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.contrib.auth import authenticate, login, logout
    from django.contrib.auth.decorators import *
    
    def webpage_login(request):
        """ 
            页面 -> 登录
        """
        if request.GET.get('next') == None:
            next_path = '/index/'
        else:
            next_path = request.GET.get('next')
        page = {'next': request.path + '?next=' + next_path}
        if request.POST.get('user') and request.POST.get('password'):
            user_str, passwd_str = request.POST.get('user'), request.POST.get('password')
            if authenticate(username = user_str, password = passwd_str):
                user_object = authenticate(username = user_str, password = passwd_str)
                login(request, user_object)
                return HttpResponseRedirect(request.GET.get('next'))
            else:
                return HttpResponse("没有用户或密码错误")
        else:
            return render_to_response('login.html', page)

    示例template:

    <body class="login-bg">
            <div class="login-body">
                <div class="login-heading">
                    <h1>Login</h1>
                </div>
                <div class="login-info">
                    <form action="{{ next }}" method="post">
                        <input type="text" class="user" name="user" placeholder="User" required="">
                        <input type="password" name="password" class="lock" placeholder="Password">
                        <input type="submit" name="Sign In" value="Login">
                    </form>
                </div>
            </div>
    </body>

    django中内置的权限控制3-许可(Permission) 和 用户组(Group)


    >> 许可(Permissions)<<

    当我们在django中安装好了auth应用之后,Django就会为每一个你安装的app中的Model创建三个权限:add/change/delete,执行python manage.py syncdb之后相应的数据会插入到数据库中的。每一次你执行syncdb,Django都会为每个用户给新出现的Model增加这三个权限。

    例如,你创建了一个应用叫做school,里面有一个模型叫做StudyGroup,那么你可以用任何一个user对象执行下面的程序,其结果都返回True:

    1 user.hash_perm('school.add_studygroup')
    2 user.hash_perm('school.change_studygroup')
    3 user.hash_perm('school.delete_studygroup')

    我们也可以自己定义一些许可,就是在Model类的meta属性中添加permissions定义。比方说,创建了一个模型类叫做Discussion,我们可以创建几个权限来对这个模型的权限许可进行控制,控制某些人可以发起讨论、发起回复,关闭讨论:

    1 class Discussion(models.Model):
    2     ...
    3     class Meta:
    4         permissions = (
    5             ("open_discussion", "Can create a discussion"),
    6             ("reply_discussion", "Can reply discussion"),
    7             ("close_discussion", "Can remove a discussion by setting its status as closed"),
    8         )

    执行manage.py syncdb就会把增加的权限信息录入到后台数据库。

    通过某一个user的user_permissions属性,permission_1为auth_permission表中的id值:

    1 user.user_permissions.add(permission_1, permission_2, ...)

    删除权限:

    1 user.user_permissions.remove(permission_1, permission_2, ...)

    通过user的一个组,然后通过group的permissions属性:

    1 group.permissions.add(permission_1, permission_2, ...)

    我们要判断一个用户是否有发讨论的权限,我们可以用下面的代码:

    1 user.has_perm('school.open_discussion')

    Permission类和User类没什么特殊的,都是普通的DjangoModel。在第一篇文章中我们详细探讨了User模型的属性和方法。在这里我们探讨一下Permission模型和如何用编程的方式而不是通过预定义然后syncdb的方式创建permission。因为也许在某些时候,需要动态创建并分配权限。

    也可以通过Permission.objects.create()方法添加对应app中models的权限,如下app为svn中的模型定义Project添加除默认三个权限外的权限:

    1 from svn.models import Project
    2 from django.contrib.auth.models import Group, Permission
    3 from django.contrib.contenttypes.models import ContentType
    4 
    5 content_type = ContentType.objects.get_for_model(Project)
    6 permission = Permission.objects.create(codename='project_admin',
    7                                        name='Department administrator',
    8                                        content_type=content_type)
  • 相关阅读:
    What is the purpose of FormsAuthenticationTicket isPersistent property?
    Forms Authentication configurations
    What is the difference between localStorage, sessionStorage, session and cookies?
    Set-Cookie
    申威机器信息
    systemctl 关闭图形界面的办法
    sourcetree 使用
    在 Web 项目中应用 Apache Shiro
    移动web开发框架
    从0开始搭建symphony
  • 原文地址:https://www.cnblogs.com/jiangxu67/p/4988304.html
Copyright © 2011-2022 走看看