Python Web开发学习我爱编程人工智能 物联网 大数据 云计算

Django权限认证系统

2018-03-27  本文已影响346人  吾星喵

Django认证系统

Cookie 与 Session

概念

cookie 不属于 http 协议范围,由于 http 协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此 cookie 就是在这样一个场景下诞生。

cookie 的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上 cookie,这样服务器就能通过 cookie 的内容来判断这个是“谁”了。

cookie 虽然在一定程度上解决了“保持状态”的需求,但是由于 cookie 本身最大支持 4096 字节,以及 cookie 本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是 session。

问题来了,基于 http 协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的 cookie 就起到桥接的作用。

我们可以给每个客户端的 cookie 分配一个唯一的 id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的 cookie 的 id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

总结而言:cookie 弥补了 http 无状态的不足,让服务器知道来的人是“谁”;但是 cookie 以文本的形式保存在本地,自身安全性较差;所以我们就通过 cookie识别不同的用户,对应的在 session 里保存私密的信息以及超过 4096 字节的文本。另外,上述所说的 cookie 和 session 其实是共通性的东西,不限于语言和框架。

登陆应用

我们在制作了一个登陆页面,在验证了用户名和密码的正确性后跳转到后台的页面。但是测试后也发现,如果绕过登陆页面。直接输入后台的url地址也可以直接访问的。这个显然是不合理的。其实我们缺失的就是 cookie 和 session 配合的验证。有了这个验证过程,我们就可以实现和其他网站一样必须登录才能进入后台页面了。 先说一下这种认证的机制。每当我们使用一款浏览器访问一个登陆页面的时候,一旦我们通过了认证。服务器端就会发送一组随机唯一的字符串(假设是 123abc )到浏览器端,这个被存储在浏览端的东西就叫 cookie。而服务器端也会自己存储一下用户当前的状态,比如 login=true,username=hahaha 之类的用户信息。但是这种存储是以字典形式存储的,字典的唯一 key 就是刚才发给用户的唯一的 cookie 值。那么如果在服务器端查看 session 信息的话,理论上就会看到如下样子的字典

{'123abc':{'login':true,'username:hahaha'}}

因为每个 cookie 都是唯一的,所以我们在电脑上换个浏览器再登陆同一个网站也需要再次验证。那么为什么说我们只是理论上看到这样子的字典呢?因为处于安全性的考虑,其实对于上面那个大字典不光key值 123abc 是被加密的,value 值 {'login':true,'username:hahaha'} 在服务器端也是一样被加密的。所以我们服务器上就算打开 session 信息看到的也是类似与以下样子的东西

{'123abc':dasdasdasd1231231da1231231}

知道了原理,下面就来用代码实现。

Django 实现的 Cookie

获取 Cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    #参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间

设置 Cookie

rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)

参数

def set_cookie(self, key,                 键
             value='',            值
             max_age=None,        超长时间
             expires=None,        超长时间
             path='/',           Cookie生效的路径,
                                         浏览器只会把cookie回传给带有该路径的页面,这样可以避免将
                                         cookie传给站点中的其他的应用。
                                         / 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问

                     domain=None,         Cookie生效的域名

                                          你可用这个参数来构造一个跨站cookie。
                                          如, domain=".example.com"
                                          所构造的cookie对下面这些站点都是可读的:
                                          www.example.com 、 www2.example.com
                         和an.other.sub.domain.example.com 。
                                          如果该参数设置为 None ,cookie只能由设置它的站点读取。

             secure=False,        如果设置为 True ,浏览器将通过HTTPS来回传cookie。
             httponly=False       只能http协议传输,无法被JavaScript获取
                                         (不是绝对,底层抓包可以获取到也可以被覆盖)
          ): pass

由于 cookie 保存在客户端的电脑上,所以,JavaScript 和 jquery 也可以操作 cookie。

<script src='/static/js/jquery.cookie.js'>
</script> $.cookie("key", value,{ path: '/' });

删除 cookie

response.delete_cookie("cookie_key",path="/",domain=name)

cookie 存储到客户端

优点:数据存在在客户端,减轻服务器端的压力,提高网站的性能。

缺点:安全性不高:在客户端机很容易被查看或破解用户会话信息

Django 实现的 Session

基本操作

1、设置Sessions值
    request.session['session_name'] ="admin"
2、获取Sessions值
    session_name = request.session["session_name"]
3、删除Sessions值
    del request.session["session_name"]
4、检测是否操作session值
    if "session_name" is request.session :
5、get(key, default=None)
    fav_color = request.session.get('fav_color', 'red')
6、pop(key)
    fav_color = request.session.pop('fav_color')
7、keys()

8、items()

9、setdefault()

10、flush() 删除当前的会话数据并删除会话的Cookie。
            这用于确保前面的会话数据不可以再次被用户的浏览器访问
            例如,django.contrib.auth.logout() 函数中就会调用它。

11 用户session的随机字符串
    request.session.session_key

    # 将所有Session失效日期小于当前日期的数据删除
    request.session.clear_expired()

    # 检查 用户session的随机字符串 在数据库中是否
    request.session.exists("session_key")

    # 删除当前用户的所有Session数据
    request.session.delete("session_key")

    request.session.set_expiry(value)
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。

流程解析

  1. 当用户登录后,生成一个字典{key, value},将字典存入session,key是自动生成的一段字符串标识,返回cookie,value是一个自定义格式化的字典;
  2. 在第1步骤生成的字典value中自定义格式来存储用户的信息,ruuser信息,is_login等;
  3. 当我们在Django中用到session时,cookie有服务器随机生成,写到浏览器的cookie中,每个浏览器都有自己的cookie值,是session寻找用户信息的唯一标识,每个浏览器请求到后台接收的request.session等价于在1中session字典的key(cookie)对应的value。

session的好处,客户端只有cookie的值,但是始终没有用户的信息。

用法:request.session.get('k1')

request.session['k1'] = 'v1'

session依赖于cookie,cookie保存在浏览器,session保存在服务器端。

-----------普通登录,登陆后跳回原url---------

models

from django.db import models

class UserInfo(models.Model):
    username = models.CharField(max_length=10, verbose_name='用户名')
    password = models.CharField(max_length=10, verbose_name='密码')

views

from django.shortcuts import render, HttpResponse, redirect, HttpResponseRedirect
from .models import UserInfo


def menu(request):
    return render(request, 'menu.html')

【views】login_session

当用户直接访问登录页面时,直接显示登录页面;

当用户未登录访问需要权限的页面时,http://172.16.66.66:8899/login_session,会自动跳转到登录页面http://172.16.66.66:8899/login_session?next=/index_session,把next设置到一个隐藏的表单next_url中

POST成功登陆后,获取next_url的值,重定向到该页面跳回。

# 使用session方式进行登录
def login_session(request):
    next_url = request.GET.get('next')  # 如果没有next默认结果是None
    print('获取跳转的页面:', next_url)
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST['password']
        print('用户登录需求:', username)
        user = UserInfo.objects.filter(username=username, password=password)
        print(user, type(user))
        if user:
            print('已注册用户')
            # 设置session内部的字典内容
            request.session['is_login'] = True
            request.session['username'] = username
            next_url = request.POST.get('next_url')
            print('POST登录后跳转:', next_url, type(next_url))
            # 登录成功就将url重定向到后台的url
            if next_url == 'None' or next_url.strip() == '':
                return redirect('/index_session')
            else:
                return redirect(next_url)
        else:
            print('未注册用户')

    # 登录不成功或第一访问就停留在登录页面
    return render(request, 'login_session.html', locals())

【views】index_session

# 登陆后的主页index_session.html
def index_session(request):
    print("\n-------session", request.session)
    print('-------cookie', request.COOKIES, '\n')
    """
    这里必须用读取字典的get()方法把is_login的value缺省设置为False,
    当用户访问index这个url先尝试获取这个浏览器对应的session中的
    is_login的值。如果对方登录成功的话,在login里就已经把is_login
    的值修改为了True,反之这个值就是False的
    """

    is_login = request.session.get('is_login', False)
    print('是否登录:', is_login)
    # 如果为真,就说明用户是正常登陆的
    if is_login:
        # 获取字典的内容并传入页面文件
        cookie_content = request.COOKIES
        session_content = request.session

        username = request.session['username']
        status = '状态登录中!'
        # print(locals(), '\n')
        return render(request, 'index_session.html', locals())

    else:
        """
        如果访问的时候没有携带正确的session,
        就直接被重定向url回login页面
        """
        # return redirect('/login_session')
        return redirect('%s?next=%s' % ('/login_session', request.path))

【views】index_session_next

# 测试session主页跳回index_session_next.html
def index_session_next(request):
    is_login = request.session.get('is_login', False)
    print('是否登录:', is_login)
    # 如果为真,就说明用户是正常登陆的
    if is_login:
        # 获取字典的内容并传入页面文件
        cookie_content = request.COOKIES
        session_content = request.session

        username = request.session['username']
        status = '状态登录中!'
        # print(locals(), '\n')
        return render(request, 'index_session_next.html', locals())

    else:
        """
        如果访问的时候没有携带正确的session,
        就直接被重定向url回login页面
        """
        # return redirect('/login_session')
        return HttpResponseRedirect('%s?next=%s' % ('/login_session', request.path))

【views】logout_session

def logout_session(requets):
    """
    直接通过request.session['is_login']回去返回的时候,
    如果is_login对应的value值不存在会导致程序异常。所以
    需要做异常处理
    """
    try:
        # 删除is_login对应的value值
        del requets.session['is_login']
        print('删除')
        # OR---->request.session.flush() # 删除django-session表中的对应一行记录
    except KeyError:
        pass

    # 点击注销之后,直接重定向回登录页面
    return redirect('/login_session')

urls

from django.conf.urls import url
from django.contrib import admin
from authsys import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login_session$', views.login_session, name='login_session'),
    url(r'^index_session$', views.index_session, name='index_session'),
    url(r'^index_session_next$', views.index_session_next, name='index_session_next'),
    url(r'^logout_session$', views.logout_session, name='logout_session'),
]

【templates】login_session.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>

<form action="/login_session" method="post">
    <p>跳转: <input type="text" name="next_url" value="{{ next_url }}">[隐藏的表单]</p>
    <p>用户名: <input type="text" name="username"></p>
    <p>密  码: <input type="password" name="password"></p>
    <p><input type="submit" value="登录"></p>
</form>
<a href="/">菜单</a>
</body>
</html>

【templates】index_session.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>auth主页</title>
</head>
<body>

<h3>{{ state }}  hello {{ request.user }}</h3>request.user.username:{{ request.user.username }}
<br>
<a href="/auth_logout">注销</a>
<a href="/">菜单</a>
</body>
</html>

【templates】index_session_next.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页session-next测试</title>
</head>
<body>

<h3>使用next进行跳转测试 {{ username }}</h3>{{ status }}
<a href="/logout_session">注销</a>
<a href="/">菜单</a>
</body>
</html>

-----------登录权限,装饰器实现-----------

models.py

class UserInfo(models.Model):
    username = models.CharField(max_length=10, verbose_name='用户名')
    password = models.CharField(max_length=10, verbose_name='密码')

    class Meta:
        verbose_name = '用户名'
        verbose_name_plural = '用户中心'

    def __str__(self):
        return str(self.username)


class PermissionGroup(models.Model):
    pg_name = models.CharField(max_length=10, verbose_name='权限名')
    pg_describe = models.CharField(max_length=20, verbose_name='权限描述')
    pg_admin = models.ForeignKey(UserInfo, verbose_name='管理人')

    class Meta:
        verbose_name = '权限'
        verbose_name_plural = '项目权限组'

    def __str__(self):
        return str(self.pg_name)


class PermissionUser(models.Model):
    pu_group = models.ForeignKey(PermissionGroup, verbose_name='权限组')
    pu_user = models.ForeignKey(UserInfo, verbose_name='成员')

    class Meta:
        verbose_name = '成员'
        verbose_name_plural = '项目组成员'

    def __str__(self):
        return str(self.pu_user)

urls.py

from django.conf.urls import url
from django.contrib import admin
from authsys import views

urlpatterns = [
    url(r'my_login_index1', views.my_login_index1, name='my_login_index1'),
    url(r'my_login_index2', views.my_login_index2, name='my_login_index2'),
    url(r'my_verify_login_index1', views.my_verify_login_index1, name='my_verify_login_index1'),
    url(r'^my_denied$', views.my_denied, name='my_denied'),
    url(r'my_verify_login_index2', views.my_verify_login_index2, name='my_verify_login_index2'),
    url(r'my_verify_login_index3', views.my_verify_login_index3, name='my_verify_login_index3'),
    url(r'my_verify_login_index4', views.my_verify_login_index4, name='my_verify_login_index4'),
    url(r'^permission_group$', views.permission_group, name='permission_group'),
    url(r'^permission_user$', views.permission_user, name='permission_user'),
]

menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页菜单</title>
</head>
<body>
<h3>登录认证</h3>
<a href="{% url 'my_login_index1' %}" >自定义login装饰器访问一</a><br>
<a href="{% url 'my_login_index2' %}" >自定义login装饰器访问二</a><br>
<br>
<h3>登录认证、权限验证</h3>
<a href="{% url 'my_verify_login_index1' %}">访问权限认证登录-权限组work</a><br>
<a href="{% url 'my_verify_login_index2' %}">访问权限认证登录-权限组study</a><br>
<a href="{% url 'my_verify_login_index3' %}">访问权限认证登录-权限组空,所有用户都能访问</a><br>
<a href="{% url 'my_verify_login_index4' %}">访问权限认证登录-权限组admin,管理员组能访问</a><br>
<br>
<a href="{% url 'permission_group' %}">权限组管理</a><br>
</body>
</html>

【views】登录验证装饰器,不传递参数

# 使用装饰器登录session,如果是已登录则直接进入视图,否则跳回登录页面进行登录后跳回
def my_login_required(view_func):
    def my_login(request, *args, **kwargs):
        print(view_func)
        redirect_url = request.path
        print('登陆后返回链接:', redirect_url)

        is_login = request.session.get('is_login', False)
        print('是否登录:', is_login)
        # 如果为真,就说明用户是正常登陆的
        if is_login:
            return view_func(request, *args, **kwargs)
        else:
            return HttpResponseRedirect('/login_session?next=%s' % redirect_url)

    return my_login

【views】登录验证,登陆后跳转原网页测试

# 要求登陆后的页面一
@my_login_required
def my_login_index1(request):
    status = '使用自定义login装饰器登录一'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())


# 要求登陆后的页面二
@my_login_required
def my_login_index2(request):
    status = '使用自定义login装饰器登录二'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())

【html】查看前面index_session.html

【views】登录权限装饰器,传递权限参数

# 装饰器验证权限登录,访问各自拥有权限的视图,如果为pg_admin,则需要改组管理员权限
def my_verify(permissions=None):  # username是传入的用户名,验证权限
    def _login_required(view_func):
        def _login(request, *args, **kwargs):
            """
            验证账号已登录成功:
            1、 登录成功判断装饰器要求的权限;
            2、 未登录跳转到登陆页面;
            """
            redirect_url = request.path
            is_login = request.session.get('is_login', False)
            print('是否登录:', is_login)
            # 如果为真,就说明用户是正常登陆的
            if is_login:
                """
                登陆成功后,权限验证:
                1、 @my_verify(),参数permissions为空,则无特殊权限,Everyone都可访问;
                2、 @my_verify('pg_admin'),参数permissions为pg_admin,允许管理员admin、在admin组的成员、各项目组管理员pg_admin进行登录;
                3、 @my_verify('work'),参数为work,允许,允许管理员admin、在admin组的成员,项目work组的管理员、项目work组的成员进行登录;
                4、 权限不在项目列表中,阻止登录;
                """
                username = request.session.get('username', '@anonymous@')
                print('登录的用户名:', username)
                if not permissions:
                    print('指定权限为空')
                    # 如果视图的权限没有定义,就验证默认权限
                    return view_func(request, *args, **kwargs)
                elif permissions == 'pg_admin':
                    print('组管理员权限登录')
                    try:
                        if username == 'admin' \
                                or PermissionUser.objects.filter(pu_user=UserInfo.objects.get(username=username), pu_group=PermissionGroup.objects.get(pg_name='admin')) \
                                or PermissionGroup.objects.filter(pg_admin=UserInfo.objects.get(username=username)):
                            return view_func(request, *args, **kwargs)
                        else:
                            print('用户未授权')
                            return HttpResponseRedirect('/my_denied')
                    except (UserInfo.DoesNotExist, PermissionGroup.DoesNotExist):
                        if PermissionGroup.DoesNotExist:
                            print('权限组有误,不存在')
                        elif UserInfo.DoesNotExist:
                            print('用户不存在')
                        return HttpResponseRedirect('/my_denied')
                elif permissions in map(lambda x: x.pg_name, PermissionGroup.objects.all()):
                    print('需要的权限【%s】在项目列表中' % permissions)
                    # 如果视图的权限定义了,就将用户的权限和预定义的权限(例如:@my_verify('work'))进行匹配
                    # PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name=permissions), pu_user=UserInfo.objects.get(username=username)) 为Ture,表示该成员有权限
                    try:
                        # 排除authsys.models.DoesNotExist: UserInfo matching query does not exist.异常产生
                        # 如果登录用户为admin,或用户在admin组里的,可直接访问
                        # 如果用户是该权限组的管理员,或者该用户在权限组内即可访问
                        if username == 'admin' \
                                or PermissionUser.objects.filter(pu_user=UserInfo.objects.get(username=username), pu_group=PermissionGroup.objects.get(pg_name='admin')) \
                                or username == PermissionGroup.objects.get(pg_name=permissions).pg_admin \
                                or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name=permissions), pu_user=UserInfo.objects.get(username=username)):
                            return view_func(request, *args, **kwargs)
                        else:
                            print('用户未授权')
                            return HttpResponseRedirect('/my_denied')
                    except (UserInfo.DoesNotExist, PermissionGroup.DoesNotExist):
                        if PermissionGroup.DoesNotExist:
                            print('权限组有误,不存在')
                        elif UserInfo.DoesNotExist:
                            print('用户不存在')
                        return HttpResponseRedirect('/my_denied')
                else:
                    print('有问题,权限不在项目列表中')
                    return HttpResponseRedirect('/my_denied')
            else:
                return HttpResponseRedirect('/login_session?next=%s' % redirect_url)
        return _login
    return _login_required

【views】登录权限参数要求项目组成员

@my_verify('work')
def my_verify_login_index1(request):
    status = '使用装饰器验证权限登录my_verify_login_index,权限组为【work】'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())


@my_verify('study')
def my_verify_login_index2(request):
    status = '使用装饰器验证权限登录my_verify_login_index,权限组为【study】'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())

【views】登录权限参数为空,都可访问

@my_verify()
def my_verify_login_index3(request):
    status = '使用装饰器验证权限登录my_verify_login_index,权限组为【空】'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())

【views】登录权限参数为admin组成员登录

@my_verify('admin')
def my_verify_login_index4(request):
    status = '使用装饰器验证权限登录my_verify_login_index,权限组为【admin】'
    username = request.session.get('username', '@anonymous@')
    return render(request, 'index_session.html', locals())

【views】登录权限被阻止

# 权限被拒绝跳转页面
def my_denied(request):
    username = request.session.get('username', '@anonymous@')
    return HttpResponse('权限被阻止 【%s】 <a href="/logout_session">注销</a> <a href="/">菜单</a>' % username)

【views】项目组管理,登录权限为pg_admin

# 权限组管理,登录用户为组管理员权限,增加删除,普通用户将无法访问
@my_verify('pg_admin')
def permission_group(request):
    username = request.session.get('username')
    if request.method == 'POST' and (username == 'admin' or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name='admin'), pu_user=UserInfo.objects.get(username=username))):
        """
        增加项目,项目管理员
        1、 当登录用户是admin、登录用户为admin组成员,可进行项目新增;
        2、 增加时,查看数据库是否已存在pg_name字段,如果存在则不执行增加;
        """
        pg_name = request.POST.get('pg_name', None)
        pg_describe = request.POST.get('pg_describe', None)
        pg_admin = request.POST.get('pg_admin', None)
        if pg_name.strip() != '' and pg_describe.strip() != '' and pg_admin.strip() != '':
            print(pg_name)
            if PermissionGroup.objects.filter(pg_name=pg_name) or PermissionGroup.objects.filter(pg_describe=pg_describe):
                state = '数据库已存在【%s】' % pg_name
            else:
                PermissionGroup.objects.create(
                    pg_name=pg_name,
                    pg_describe=pg_describe,
                    pg_admin=UserInfo.objects.get(username=pg_admin),
                )
                state = '增加【%s】权限组成功' % pg_name
        else:
            state = '提交字段不能为空'
            print(state)
    """
    项目删除
    1、 删除的名称存在、删除的名称不是admin组、且登录用户为admin或admin组的成员,才能进行删除;
    2、 如果删除的名称不存在,则不进行任何操作;
    3、 如果登录用户不是以上要求,则不允许删除;
    """
    del_name = request.GET.get('del', None)
    if del_name and del_name != 'admin' and (username == 'admin' or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name='admin'), pu_user=UserInfo.objects.get(username=username))):
        # 当需要删除的字段不为空,且删除的不是admin权限组,登录用户为amdin或在admin权限组的,即可删除
        PermissionGroup.objects.filter(pg_name=del_name).delete()
        state = '删除【%s】权限组成功' % del_name
    elif not del_name:
        pass
    else:
        state = '删除【%s】权限组失败,权限不足' % del_name
    # 判断用户所在组,如果是管理员或在管理员组,则拥有所有权限,否则只有用组管理员权限,当用户不在组管理员表时,则拒绝权限
    """
    前端页面显示
    1、 登录用户为admin或admin组的成员,则显示所有项目组信息,且is_admin为True,显示增加项目表单;
    2、 如果登录用户是普通项目管理员,则只显示该项目管理员下的所有项目信息,且不显示增加项目表单;
    3、 如果是普通用户,则组织其他操作;
    """
    if username == 'admin' or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name='admin'), pu_user=UserInfo.objects.get(username=username)):
        # 如果登录用户是admin,或者登录用户在admin权限组内,则显示全部项目,且前端显示增加项目
        pg_list = PermissionGroup.objects.all().order_by('pg_name',)
        is_admin = True
    elif PermissionGroup.objects.filter(pg_admin=UserInfo.objects.get(username=username)):
        # 如果登录用户是某个项目的管理员,则显示该管理员所有项目,且不再前端显示增加项目
        pg_list = PermissionGroup.objects.filter(pg_admin=UserInfo.objects.get(username=username))
        is_admin = False
    else:
        return HttpResponseRedirect('/my_denied')
    user_list = UserInfo.objects.all().order_by('username',)
    return render(request, 'permission_group.html', locals())

【html】 项目组管理前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>权限组管理</title>
</head>
<body>
{% if is_admin is True %}
<form action="/permission_group" method="post">
    <p>权限字段: <input type="text" name="pg_name" value=""></p>
    <p>权限名称: <input type="text" name="pg_describe"></p>
    <p>管理人员:
    <select name="pg_admin">
        {% for user in user_list %}
            <option>{{ user.username }}</option>
        {% endfor %}
    </select>
    </p>
    <p><input type="submit" value="增加"></p>
</form>
{% endif %}
<p>当前登录用户:{{ request.session.username }}</p>
<table border="1">
    <tr>
        <th>项目字段</th>
        <th>项目描述</th>
        <th>项目管理员</th>
        <th>项目成员</th>
        <th>项目管理</th>
    </tr>
    {% for pg in pg_list %}
    <tr>
        <td>{{ pg.pg_name }}</td>
        <td>{{ pg.pg_describe }}</td>
        <td>{{ pg.pg_admin.username }}</td>
        <td><a href="{%  url 'permission_user' %}?group={{ pg.pg_name }}">成员管理</a></td>
        {% if pg.pg_name != 'admin' %}
        <td><a href="{% url 'permission_group' %}?del={{ pg.pg_name }}" onclick="javascript:return del();">删除</a></td>
        {% else %}
        <td></td>
        {% endif %}
    </tr>
    {% endfor %}
</table>
<br>
{{ state }}
<br>
<a href="{% url 'permission_group' %}">查询</a>
<a href="/logout_session">注销</a>
<a href="/">菜单</a>

<script type="text/javascript">
function del() {
var msg = "您真的确定要删除吗?\n\n请确认!";
if (confirm(msg)==true){
        return true;
    }else{
        return false;
    }
}
</script>

</body>
</html>

【views】项目组成员管理,权限为pg_admin

# 成员管理
@my_verify('pg_admin')
def permission_user(request):
    username = request.session.get('username')
    group = request.GET.get('group', None)
    del_user = request.GET.get('del_user', None)
    if request.method == 'POST':
        add_user = request.POST.get('add_user', None)
        if add_user and not PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name=group), pu_user=UserInfo.objects.get(username=add_user)):
            # 如果增加的用户名不为空,且数据库中该权限组没有该用户,则增加
            PermissionUser.objects.create(
                pu_group=PermissionGroup.objects.get(pg_name=group),
                pu_user=UserInfo.objects.get(username=add_user)
            )
            state = '添加成员成功【%s】' % add_user
        elif not add_user:
            state = '数据POST异常'
        else:
            state = '数据库中已有成员【%s】' % add_user
    """
    删除项目组成员
    1、 满足删除用户不为空、且登录用户为admin或admin组成员或该操作当前项目组的管理员;
    2、 如果del_user为空,则不进行其他操作;
    3、 如果以上条件,则权限不足;
    """
    if del_user \
            and \
            (username == 'admin'
                or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name='admin'), pu_user=UserInfo.objects.get(username=username))
                or PermissionGroup.objects.filter(pg_name=group, pg_admin=UserInfo.objects.get(username=username))):
        print('删除成员【%s】' % del_user)
        state = '删除成员【%s】' % del_user
        PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name=group), pu_user=UserInfo.objects.get(username=del_user)).delete()
    elif not del_user:
        pass
    else:
        state = '权限不足,不允许删除成员【%s】' % del_user
    print('操作的组:', group)
    """
    显示项目组成员
    1、 登录用户为admin或admin组成员或该操作当前项目组的管理员,将显示该项目组的成员列表,且显示增加成员的表单;
    2、 否则不显示成员列表,不显示增加成员表单
    """
    if username == 'admin' \
            or PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name='admin'), pu_user=UserInfo.objects.get(username=username)) \
            or PermissionGroup.objects.filter(pg_name=group, pg_admin=UserInfo.objects.get(username=username)):
        pu_list = PermissionUser.objects.filter(pu_group=PermissionGroup.objects.get(pg_name=group))
        can_add_user = True
    else:
        state = '权限不足,不显示内容'
        can_add_user = False
    pg_describe = PermissionGroup.objects.get(pg_name=group).pg_describe
    user_list = UserInfo.objects.all()
    return render(request, 'permission_user.html', locals())

【html】 项目组成员管理前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组成员管理</title>
</head>
<body>
<p>当前登录用户:{{ request.session.username }}</p>
<p>项目名称:【{{ pg_describe }}】</p>
<p>项目字段:【{{ group }}】</p>
<table border="1">
    <tr>
        <!--<th>项目字段</th>-->
        <th>项目成员</th>
        <th>成员管理</th>
    </tr>
{% for pu in pu_list %}
<tr>
    <!--<td>{{ pu.pu_group.pg_name }}</td>-->
    <td>{{ pu.pu_user.username }}</td>
    <td><a href="{%  url 'permission_user' %}?group={{ pu.pu_group.pg_name }}&del_user={{ pu.pu_user.username }}" onclick="javascript:return del();">删除成员</a> </td>

</tr>
{% endfor %}
</table>
<br>
{% if can_add_user is True %}
<form action="/permission_user?group={{ group }}" method="post">
    <p>权限组: <input type="text" name="group" value="{{ group }}" readonly>隐藏</p>
    <p>增加成员:
    <select name="add_user">
        {% for user in user_list %}
            <option>{{ user.username }}</option>
        {% endfor %}
    </select>
    </p>
    <p><input type="submit" value="增加"></p>
</form>
{% endif %}
{{ state }}
<br>
<a href="/logout_session">注销</a>
<a href="/">菜单</a>

<script type="text/javascript">
function del() {
var msg = "您真的确定要删除吗?\n\n请确认!";
if (confirm(msg)==true){
        return true;
    }else{
        return false;
    }
}
</script>

</body>
</html>

session 存储的相关配置

数据库配置(默认)

Django 默认支持 Session,并且默认是将 Session 数据存储在数据库中,即:django_session 表中。

配置 settings.py

SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)

缓存配置

配置 settings.py

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存

文件配置

配置 settings.py

SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存

用户认证

Django 实现用户认证auth

views

# 用户认证-auth 模块
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.contrib.auth.models import User

【views】auth_login

提供了用户认证,即验证用户名以及密码是否正确,一般需要 username password 两个关键字参数

如果认证信息有效,会返回一个 User 对象。authenticate() 会在 User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过 authenticate() 的User 对象会报错的!!

user = authenticate(username='someone',password='somepassword')

login(HttpRequest, user)

该函数接受一个 HttpRequest 对象,以及一个认证了的 User 对象

此函数使用 django 的 session 框架给某个已认证的用户附加上 session id 等信息。

# 用户登录,登录成功跳转主页
def auth_login(request):
    """
    提供了用户认证,即验证用户名以及密码是否正确,一般需要 username  password 两个关键字参数
    """
    username = request.POST.get('user')
    # password = request.POST['pwd']  # 不知道为什么不能用
    password = request.POST.get('pwd')

    login_user = authenticate(username=username, password=password)
    print('用户状态:', login_user)
    if login_user is not None:
        login(request, login_user)
        next_url = request.POST.get('next_url')
        if next_url.strip() != '':
            # 登录成功跳回登录前的页面
            return redirect(next_url)
        else:
            # 登录成功,跳转到主页
            return redirect('/auth_index')

    next_url = request.GET.get('next', '')  # 访问登录,首先获取next的值,如果没有,则next_url赋值为''
    return render(request, 'auth_login.html', locals())

【views】auth_index

要求:

1.用户登陆后才能访问某些页面,
2.如果用户没有登录就访问该页面的话直接跳到登录页面
3.用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址(这个需要在登录视图中设置)

如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。

通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用 request.user.is_authenticated() 判断用户是否已经登录,如果 true 则可以向前台展示 request.user.name

# 登录跳转,方法一
def auth_index(request):
    print('登录用户:', request.user)
    print('登录用户:', request.user.username)
    print('是否登录:', request.user.is_authenticated())  # bool值,判断用户是否登录
    """
    如果用户登录成功,显示主页,否则返回登录界面
    直接访问:http://172.16.66.66:8899/auth_index
    如果未登录则会跳转:http://172.16.66.66:8899/auth_login?next=/auth_index
    """
    state = '使用普通next跳转,默认'
    if not request.user.is_authenticated():
        # return redirect('%s?next=%s' % ('/auth_login', request.path))
        return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
    else:
        return render(request, 'auth_index.html',
                      {
                          'state': state,
                      })

【views】auth_index_required

User 对象:

User 对象属性:username, password(必填项)password 用哈希算法保存到数据库

is_staff : 用户是否拥有网站的管理权限.

is_active : 是否允许用户登录, 设置为False,可以不用删除用户来禁止 用户登录.

# 登录跳转:方法二;django 已经为我们设计好了一个用于此种情况的装饰器:login_requierd()
@login_required
def auth_index_required(request):
    """
    如果用户已登录才会进入该视图,否则跳转到 django 默认的 登录 URL '/accounts/login/ '
    (这个值可以在 settings 文件中通过 LOGIN_URL 进行修改)
    LOGIN_URL = '/auth_login'
    直接访问:http://172.16.66.66:8899/auth_index_required
    如果未登录则会跳转:http://172.16.66.66:8899/auth_login?next=/auth_index_required
    """
    state = '使用装饰器跳转'
    return render(request, 'auth_index.html', locals())

【views】auth_logout

logout(request) 注销用户

# 注销用户
def auth_logout(request):
    logout(request)
    return redirect('/auth_login')

【views】auth_register

# 注册用户
def auth_register(request):
    state = None
    if request.method == 'POST':
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email = request.POST.get('email', '')

        if User.objects.filter(username=username):
            state = '用户已存在'
        elif password != repeat_password:
            state = '新密码不一致'
        else:
            new_user = User.objects.create_user(username=username, password=password, email=email)
            new_user.save()
            return redirect('/auth_login')

    content = {
        'state': state,
        'user': None
    }

    return render(request, 'auth_register.html', content)

【views】auth_resetpass

用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True

使用 set_password() 来修改密码

# 修改密码
@login_required
def auth_resetpass(request):
    login_user = request.user
    print('用户修改密码:', login_user)
    state = None

    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        if login_user.check_password(old_password):  # 检查原密码符合再进行密码修改
            if not new_password:
                state = '新密码为空'
            elif new_password != repeat_password:
                state = '新密码不一致'
            else:
                login_user.set_password(new_password)
                login_user.save()
                return redirect('/auth_login')
        else:
            state = '原密码错误'

    content = {
        'user': login_user,
        'state': state,
    }
    return render(request, 'auth_resetpass.html', content)

urls

from django.conf.urls import url
from django.contrib import admin
from authsys import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^auth_login$', views.auth_login, name='auth_login'),
    url(r'^auth_index$', views.auth_index, name='auth_index'),
    url(r'^auth_index_required$', views.auth_index_required, name='auth_index_required'),
    url(r'^auth_logout$', views.auth_logout, name='auth_logout'),
    url(r'^auth_register$', views.auth_register, name='auth_register'),
    url(r'^auth_resetpass$', views.auth_resetpass, name='auth_resetpass'),
    url(r'^$', views.menu, name='menu'),
]

【templates】auth_login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户认证登录</title>
</head>
<body>

<form action="/auth_login" method="post">
    <p>跳转: <input type="text" name="next_url" value="{{ next_url }}">[隐藏的表单]</p>
    <p>用户名: <input type="text" name="user"></p>
    <p>密  码: <input type="password" name="pwd"></p>
    <p><input type="submit" value="登录"></p>
</form>
<a href="/">菜单</a>
</body>
</html>

【templates】auth_index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>auth主页</title>
</head>
<body>

<h3>{{ state }}  hello {{ request.user }}</h3>request.user.username:{{ request.user.username }}
<br>
<a href="/auth_logout">注销</a>
<a href="/">菜单</a>
</body>
</html>

【templates】auth_register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>auth用户注册</title>
</head>
<body>
{% if state is not None %}
<h3>{{ state }}</h3>
{% endif %}
<form action="/auth_register" method="post">
    <p>用户名: <input type="text" name="username"></p>
    <p>密  码: <input type="password" name="password"></p>
    <p>重复密码: <input type="password" name="repeat_password"></p>
    <p>邮  箱: <input type="email" name="email"></p>
    <p><input type="submit" value="注册"></p>
</form>
<a href="/">菜单</a>
</body>
</html>

【templates】auth_resetpass.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>auth密码修改</title>
</head>
<body>
{% if state is not None %}
<h3>{{ state }}</h3>
{% endif %}
<p>登录用户:{{ user }}</p>
<form action="/auth_resetpass" method="post">
    <p>旧密码: <input type="password" name="old_password"></p>
    <p>新密码: <input type="password" name="new_password"></p>
    <p>重复密码: <input type="password" name="repeat_password"></p>
    <p><input type="submit" value="修改"></p>
</form>
<a href="/">菜单</a>
</body>
</html>

【templates】menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页菜单</title>
</head>
<body>
<a href="{% url 'index_session' %}">session主页</a><br>
<a href="{% url 'index_session_next' %}">session主页-测试next</a><br>
<br>
<a href="{% url 'auth_login' %}">auth登录</a><br>
<a href="{% url 'auth_index' %}">auth主页</a><br>
<a href="{% url 'auth_index_required' %}">auth主页-装饰器</a><br>
<a href="{% url 'auth_register' %}">auth注册</a><br>
<a href="{% url 'auth_resetpass' %}">auth改密</a><br>
</body>
</html>

【感谢给予参考的文章,若侵犯版权,请联系我删除】

最后插播一则广告:作为程序员,大家要多喝蜂蜜,特别是品相好的蜂蜜,调解身体机能,写代码更带劲。【原生乡蜜】

分享淘宝店铺.png
上一篇下一篇

猜你喜欢

热点阅读