casbin工作模式:
PERM模型是由4个基础(Policy,Effect,Request,Matchers)描述各个资源和用户之间的相互关系。
P–>Policy–>策略
E–>Effect–>匹配模型
R–>Request–>请求
M–>Matchers–>匹配规则
先来介绍一下casbin
你以为我会写,没意思,自己看文档去吧
我主要上代码讲解里面的使用及注意的问题
梳理一下大只要做的几件事,按照顺序来的哈
第一:定义自己的user对象,也就是给请求添加user属性,就是request.user
第二:写一个中间件,过滤所有的权限和认证。这里是关键步骤。
第二步又分为:
编写自己的匹配规则,即xxx.conf文件和数据库的adapter,注意我这里使用的不是和csv配合,是数据库凹。
写自己的数据库model模型,文档里有提到,自己个去瞄吧。总结来说,就是写两个类:
存储规则的model,功效和csv一样,不过换了个形式而已。这都不懂了,面壁思过两秒。
还有Adapter类
完事!!!
下面直接上代码了
[request_definition]
r = sub, obj, act, app
[policy_definition]
p = sub, obj, act, app
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = regexMatch(r.sub, p.sub) && regexMatch(r.obj, p.obj) && regexMatch(r.act, p.act) && regexMatch(r.app, p.app)
请你注意,如果你使用cv大法的话,也请你和源文档的对比一下有什么不同,不要傻不拉几的直接用,而导致一直报错,回头又怪我的代码有问题,这里只说一次,后面自己问题自己解决。
我这里request_definition和policy_definition后面的app是根据我的需求自己加的哈,你他喵的自己看着啊
from django.db import models
from casbin.persist import Adapter as _Adapter, load_policy_line
class RRECasBinRule(models.Model):
ptype = models.CharField(max_length=255, null=True)
v0 = models.CharField(max_length=255, null=True)
v1 = models.CharField(max_length=255, null=True)
v2 = models.CharField(max_length=255, null=True)
v3 = models.CharField(max_length=255, null=True)
v4 = models.IntegerField()
v5 = models.CharField(max_length=255, null=True)
class Meta:
db_table = 'rre_casbin_rule'
def __str__(self):
return reduce(lambda x, y: str(x) + ', ' + str(y) if y else x,
[self.ptype, self.v0, self.v1, self.v2, self.v3, self.v4, self.v5])
def __repr__(self):
if not self.id:
return "<{cls}: {desc}>".format(cls=self.__class__.__name__, desc=self)
return "<{cls} {pk}: {desc}>".format(cls=self.__class__.__name__, pk=self.id, desc=self)
class Adapter(_Adapter):
def __init__(self):
self.rule = RRECasBinRule
self.request = None
def parse_request(self, request, *args, **kwargs):
self.request = request
def load_policy(self, model, **kwargs):
if not kwargs:
for line in self.rule.objects.all():
load_policy_line(str(line), model)
return
# 正则方式: 暂时舍弃
# v0 = "^" + kwargs.get("request").user.role + "$"
# 关键字匹配
v0 = kwargs.get("request").user.role
v3 = kwargs.get("request").headers.get("App", None)
for line in self.rule.objects.filter(v0=v0, v3=v3):
# 正则方式
# load_policy_line(str(line), model)
# 关键字匹配
s = ",".join(str(line).split(",")[:5])
load_policy_line(s, model)
# 正则方式舍弃
# for line in self.rule.objects.filter(id=1):
# load_policy_line(str(line), model)
# 读取用户分配的权限列表
roles = CasbinUserRole.objects.filter(user_id=kwargs.get("request").user.user_id)
for line in roles:
ss = ",".join(str(line).split(",")[:5])
load_policy_line(ss, model)
def _save_policy_line(self, ptype, rule):
# 正则方式
# data = dict(zip(['ptype', 'v0', 'v1', 'v2', 'v3', 'user_id'], rule))
# 关键字匹配
data = dict(zip(['ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'user_id'], rule))
try:
res = CasbinUserRole.objects.filter(**data)
if res:
raise RoleExistsError('user has already had this role')
c = CasbinUserRole.objects.create(**data)
except RoleExistsError:
raise
except Exception as e:
traceback.print_exc(e)
return False
return True
def save_policy(self, model):
"""saves all policy rules to the storage."""
for sec in ["p", "g"]:
if sec not in model.model.keys():
continue
for ptype, ast in model.model[sec].items():
for rule in ast.policy:
self._save_policy_line(ptype, rule)
return True
def add_policy(self, sec, ptype, rule):
"""adds a policy rule to the storage."""
return self._save_policy_line(ptype, rule)
def remove_policy(self, sec, ptype, rule):
"""removes a policy rule from the storage."""
if sec in ["p", "g"]:
data = dict(zip(['v0', 'v1', 'v2', 'v3', 'v4', 'user_id'], rule))
res = CasbinUserRole.objects.filter(**data).delete()
if res:
return True
return False
def remove_filtered_policy(self, sec, ptype, field_index, *field_values):
"""removes policy rules that match the filter from the storage.
This is part of the Auto-Save feature.
"""
pass
adapter = Adapter()
这是关键的两个类
# 把AuthenticationMiddleware导出来。注意我放置中间件的先后顺序
from django.contrib.auth.middleware import AuthenticationMiddleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
"codex.middleware.middle.RequestUserMiddleware",
"codex.middleware.middle.TokenMiddleware",
# "codex.middleware.middle.MYLoginMiddleware",
# "codex.middleware.middle.RoleMiddleware",
]
# 开始欣赏其表现
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
"""
点击去看这个类,主要是实现get_user来生成request.user。再去看SimpleLazyObject类。自己去看哈,其实这个玩意没啥用,把源码copy出来自己写就行了。注意些要实现的是get_user方法啊。让我们看看get_user
"""
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
"""
再看一下里面的auth.get_user(request)方法
"""
def get_user(request):
"""
Return the user model instance associated with the given request session.
If no user is retrieved, return an instance of `AnonymousUser`.
"""
from .models import AnonymousUser
user = None
try:
user_id = _get_user_session_key(request)
backend_path = request.session[BACKEND_SESSION_KEY]
except KeyError:
pass
else:
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id)
# Verify the session
if hasattr(user, 'get_session_auth_hash'):
session_hash = request.session.get(HASH_SESSION_KEY)
session_hash_verified = session_hash and constant_time_compare(
session_hash,
user.get_session_auth_hash()
)
if not session_hash_verified:
request.session.flush()
user = None
return user or AnonymousUser()
"""
看到这里return user or AnonymousUser(),你要是还不明白怎么去搞一个自己的request.user,那么神仙姐姐也救不了你了。怎么做呢,重写这个方法啊。
然后继承封装在调用,不就完事了。没事就看看源码呢,有干货在里面。
"""
from xx.models import adapter, RRECasBinRule
from django.utils.functional import SimpleLazyObject
from kp_account.models import xx, yy, M, zz
def get_user(request, user):
if not hasattr(request, '_cached_user'):
request._cached_user = RequestUser(user)
return request._cached_user
class RequestUser(BaseHandler):
role = 'anonymous' # 角色名
available = False # 逻辑删除
def __init__(self, user):
if user:
self.user_init(user)
@classmethod
def __getattr__(cls, item):
return cls.__dict__.get(item, None)
@classmethod
def user_init(cls, user):
user_id = user.get("xx", "") #自己将xx改为自定义的字段
user_name = user.get("xx", "") #自己将xx改为自定义的字段
try:
if user_id > 0 and isinstance(user_id, int):
user_info = xx.objects.get(user_id=user_id)
if user_info.role_id == 3 and user_info.company_id:
company_info = xx.objects.get(cp_code=user_info.company_id)
if time.time() - cls.str2tms(
str(company_info.time_create.replace(microsecond=0))) > constants.TRIAL:
yy.objects.update({"cp_company_role": M.get_value("unauthorized")})
yy.objects.update({"role_id": M.get_value("unauthorized")})
user_info.role_id = 2
# 读取user_id,在对应的数据库表里查询角色信息
user_role_id = user_info.role_id
role_name = M.get_key(user_role_id)
cls.role = role_name if role_name else "unauthorized"
except MacciAccountRole.DoesNotExist:
cls.role = "unauthorized"
except KPRole.DoesNotExist:
cls.role = "unauthorized"
cls.user_id = user_id # user_id
cls.username = user_name # 用户名
class RequestUserMiddleware(MiddlewareMixin):
def process_request(self, request):
# 校验token, 其中带有user信息
token = request.headers.get('Authorization', '') or request.COOKIES.get('Authorization')
user_dict = cache.get(token) if token else None
# 正常则将用户信息和权限,符合AnonymousUser形式
request.user = SimpleLazyObject(lambda: get_user(request, user_dict))
# request.user = get_user(request, user_dict)
print(request.user)
#在校验权限中间件之前先封装一个request.user对象
class RequestUserMiddleware(MiddlewareMixin):
def process_request(self, request):
# 校验token, 其中带有user信息
token = request.headers.get('Authorization', '') or request.COOKIES.get('Authorization')
user_dict = cache.get(token) if token else None
# 正常则将用户信息和权限,符合AnonymousUser形式
request.user = SimpleLazyObject(lambda: get_user(request, user_dict)) # 这里是主要的执行函数
# request.user = get_user(request, user_dict)
print(request.user)
#校验权限的中间件
class TokenMiddleware(MiddlewareMixin):
enforcer = casbin.Enforcer("auth_path/restful_model.conf", adapter)
def process_request(self, request):
"""登陆验证"""
if not self.check_permission(request):
return KPResponse({'code': 2, 'data': None, 'msg': "无权限操作!"}, content_type='application/json')
print(request.path, request.body)
def m_load_policy(self, request):
"""reloads the policy from file/database."""
self.enforcer.model.clear_policy()
self.enforcer.adapter.load_policy(self.enforcer.model, request=request)
self.enforcer.model.print_policy()
if self.enforcer.auto_build_role_links:
self.enforcer.build_role_links()
def check_permission(self, request):
# Customize it based on your authentication method.
path = request.path
method = request.method
role = request.user.role
app = request.headers.get("xx", "xx")
# 两种不改变源类的将自己的参数传入到目的位置的方法,学着点。。。
# 第一种
self.m_load_policy(request)
# 第二种 风险,有线程数据安全问题, 具体自个去看源码分析。
# self.enforcer.adapter.parse_request(request)
# self.enforcer.load_policy()
print(self.enforcer.enforce(role, path, method, app), role, path, method, app)
return self.enforcer.enforce(role, path, method, app)
如果您觉得文章对您有所帮助,可以请囊中羞涩的博主吃个鸡腿饭,万分感谢。愿每一个来到这里的人生活幸福美满。