前言:
权限是什么?
对于web程序来说,一条权限 = 一个动作 =一个url + 一种请求方法(get/post/put...) + 若干个请求参数(?name="name"&sex=1&age=10 ...)
用户访问的每个不同的 fullpath就决定了用户能获取到什么;通过区分fullpath就可以做到web程序的权限控制;
一、Django的auth_permission表
Django程序在migrate数据库之后,会自动生成一张auth_permission表,这张表专门用户存储用户权限信息;
那么如何在这张表里新增权限记录呢?
在model.py的任何一张表下增加class Meta:
class Customer(models.Model): ''' 客户信息表 ''' date=models.DateTimeField(auto_now_add=True,verbose_name='合作时间') name = models.CharField(max_length=266, verbose_name='客户姓名') customer_detail=models.ForeignKey('CustomerDetail',verbose_name='客户详细信息') def __str__(self): return self.name class Meta: # 用户和用户所有的权限,通过Django admin的user表关联起来! permissions = ( #权限名称 #权限描述! ('view_customer_list', '可以查看客户列表1111'), ('view_customer_info', '可查看用客户详细信息11111'), ('edit_own_customer_info', '可以删除客户信息111111'),)
二、Django admin 让权限关联上用户
1.auth_permission表和admin自带的auth_group、user表是M2M关系,
2.所以就可以通过Django admin直接给 用户/用户组 添加权限了;
2.给用户关联权限成功之后,user对象的has_perm()就可以去检测该用户 否拥有某个权限?
import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "permission_system.settings") import django django.setup() from app01 import models user_obj=models.UserInfo.objects.all()[1] print(user_obj) #注意superuser 拥有任何权限,必须是普通用户调用 has_perm() print(user_obj.has_perm('app01.view_customer_list')) #True
三、数据结构设计,让用户权限名 关联上Django的每个url;
通过以上步骤
每个用户仅仅是和权限名称关联了起来,但是真正的权限是url啊!但url又不是固定的;
所以得 自己设计数据结构 让 用户权限名 关联上url别名;(url别名具有固定标识作用)
perm_dic = { # auth_permissions表权限 url的别名 请求方法 携带参数?name='2GO' 'view_customer_list': ['customer_list','GET',[]], 'view_customer_info': ['customer_detail','GET',[]], 'edit_own_customer_info': ['customer_detail','POST',['qq','nam']], }
四、通过给视图函数增加装饰器验证用户是否 拥有访问权限?
让 每个url、url请求方式、?=携带参数,(完整URL) 和用户权限关联之后,就可以在用户访问Django程序的时候,
抓取到 request.path_info、request.method、request.GET/POST.get(‘args’) 和自己设计好的 数据结构去math了;
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Alex Li from django.core.urlresolvers import resolve from django.shortcuts import render perm_dic = { 'view_customer_list': ['customer_list','GET',[]], 'view_customer_info': ['customer_detail','GET',[]], 'edit_own_customer_info': ['customer_detail','POST',['qq','nam']], } def perm_check(*args,**kwargs): request = args[0] url_resovle_obj = resolve(request.path_info) current_url_namespace = url_resovle_obj.url_name #app_name = url_resovle_obj.app_name #use this name later print("url namespace:",current_url_namespace) matched_flag = False # find matched perm item matched_perm_key = None if current_url_namespace is not None:#if didn't set the url namespace, permission doesn't work print("find perm...") for perm_key in perm_dic: perm_val = perm_dic[perm_key] if len(perm_val) == 3:#otherwise invalid perm data format url_namespace,request_method,request_args = perm_val print(url_namespace,current_url_namespace) if url_namespace == current_url_namespace: #matched the url if request.method == request_method:#matched request method if not request_args:#if empty , pass matched_flag = True matched_perm_key = perm_key print('mtched...') break #no need looking for other perms else: for request_arg in request_args: #might has many args request_method_func = getattr(request,request_method) #get or post mostly #print("----->>>",request_method_func.get(request_arg)) if request_method_func.get(request_arg) is not None: matched_flag = True # the arg in set in perm item must be provided in request data else: matched_flag = False print("request arg [%s] not matched" % request_arg) break #no need go further if matched_flag == True: # means passed permission check ,no need check others print("--passed permission check--") matched_perm_key = perm_key break else:#permission doesn't work return True if matched_flag == True: #pass permission check perm_str = "app01.%s" %(matched_perm_key) #crm.view_customer_list if request.user.has_perm(perm_str): print("\033[42;1m--------passed permission check----\033[0m") return True else: print("\033[41;1m ----- no permission ----\033[0m") print(request.user,perm_str) return False else: print("\033[41;1m ----- no matched permission ----\033[0m") def check_permission(func): def wrapper(*args,**kwargs): print('---start check perm---') if perm_check(*args,**kwargs) is not True:#no permisssion return render(args[0],'403.html') return func(*args,**kwargs) return wrapper
五、权限插件和视图的无缝对接
权限插件完成之后,如何在不修改代码的前提下,通用得 结合到每个项目中呢?使用装饰器;
from django.shortcuts import render,HttpResponse,redirect from app01 import models from app01.permission import check_permission from django.contrib.auth import authenticate,login,logout from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt def acc_login(request): error = '' if request.method == "POST": username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username,password=password) if user: login(request, user) return redirect('/index/') else: error = "Wrong username or password!" return render(request,'login.html',{'error':error }) @login_required def index(request): return render(request,'index.html') @login_required @check_permission def customer_list(request): return render(request,'customer_list.html') @login_required @check_permission def customer_detail(request): return render(request,'customer_detail.html')
六、权限极致到按钮
如果权限精细到1个URL我感觉还是不够,还可以精细到 1个URL返回的标签;
def process_response(self, request, response): if request.path_info =='/arya/cmdb/worker_order/': BS=BeautifulSoup(response.content,"html5lib") tags=BS.find_all(class_='btn') for tag in tags: tag.decompose() response.content=str(BS) return response
def process_response(self, request, response): #返回相关子工单、父工单返回逻辑 if request.path_info == '/arya/cmdb/worker_order/': current_list_url = '/arya/cmdb/worker_order/' curent_page=request.GET.get('page','1') request.session['curent_page']=curent_page if request.path_info == '/arya/cmdb/worker_order/see/': current_see_url = '/arya/cmdb/worker_order/see/' BS = BeautifulSoup(response.content,"html5lib") current_obj=models.Worker_order.objects.get(pk=request.GET.get('id')) if current_obj.parent:#子工单 go_back_url=current_see_url+'?id='+str(current_obj.parent.pk) else:#就是父工单 page_number=request.session['curent_page'] go_back_url ='/arya/cmdb/worker_order/?page=' + str(page_number) BS.find(id='go_back').attrs['href'] = go_back_url response.content = str(BS) return response
参考:
https://www.cnblogs.com/alex3714/articles/6661911.html