Django权限认证系统
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失效策略。
流程解析
- 当用户登录后,生成一个字典{key, value},将字典存入session,key是自动生成的一段字符串标识,返回cookie,value是一个自定义格式化的字典;
- 在第1步骤生成的字典value中自定义格式来存储用户的信息,ruuser信息,is_login等;
- 当我们在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>
【感谢给予参考的文章,若侵犯版权,请联系我删除】
最后插播一则广告:作为程序员,大家要多喝蜂蜜,特别是品相好的蜂蜜,调解身体机能,写代码更带劲。【原生乡蜜】