【Django】RBAC权限管理系统模块-理解

今天文章分为两部分 :)

PART1 RBAC权限管理内容分享PART2 关于字节跳动一面

 

   

 

10 Minutes  Django-RBAC:

PART 1 

这权限管理系统主要功能是什么?

顾名思义,在系统中可以灵活的划分角色组,可以根据功能特性来划分:- 比如设置系统管理员角色,绑定系统所有菜单,该角色享有系统全部管理功能;- 也可以根据工作职能来划分角色,比如设置行政管理角色,绑定流程管理功能;- 权限的划分可以细致到 数据的浏览、新建、删除、修改等操作行为 - 用户可以关联多个角色组,并继承多个角色组的权限🔒

 

本文更多的是对Django的权限扩展以及如何通过扩展解决实际的业务问题展开讨论,并不会对Django权限系统的具体实现和使用方法进行介绍,这方面知识未储备好的,请先自行脑补。因为学长上周做了一套关于权限的验证方法,感到十分痛苦,以此记录一下相关内容,为了让各位参考,也为社会进步。文章内容较长,各位同学请心里做好准备🐖

 

一、Django的权限系统

在介绍Django的权限系统前,我们先来认识一下RBAC。RBAC全称Role-Based Access Control,即基于角色的访问控制。太学术的东西就不讲了,我们可以从企业运营的角度来理解RBAC,企业运营过程中,需要做很多的事情来达成企业的目标,但这些事情并非所有人都可以去做的,比如业务人员不可能去处理公司的财务问题,所以需要给要做的这些事情设置相应的权限,然后将这些权限分配给指定的人,但问题来了,如果某个员工离职了,换了一个新员工来顶替他的位置,这时你需要一一的告诉该新员工他应该做什么,他不能做什么,当工作项非常多的时候,这将是非常繁琐的过程,且非常容易出错,这时就引入了角色的概念,角色大家可以理解为企业管理中的岗位,通过将工作权限分配给岗位,再将员工安排至相应的工作岗位,那么该员工就拥有了该岗位应有的权限,当人员变动时,只需要将新员工安排至该岗位即可,整个过程就变得简单多了。这时你也许会问,那岗位的工作内容变动时,你不是一样需要重新给岗位一一分配权限吗,增加了角色反而增加了系统的复杂性?话是没错,但在企业管理中,岗位是相对稳定的,而人员则是易变的,所以引入角色将极大的提升系统的灵活性和易用性。关于RBAC就简单介绍到这,当然RBAC还包含很多的内容,希望深入了解的朋友可以Google相关的信息。⛽

Django的权限系统,可以认为是轻量级的RBAC系统,其中组可以对应RBAC的角色,通过将权限分配给组,再将用户分配到组中,从而实现了授权的过程,同时Django还支持直接对用户进行授权,这对应对一些特殊情况是十分有用的。关于Django的权限使用的详情,可以参考官方文档,在此不做详述🔑

Django的权限系统实现了最基本的权限需求,但在实际的业务开发过程中,仍然存在许多无法满足的需求,好在Django是一套异常强大的系统,系统内部提供了一套灵活的机制,使得开发人员可以方便的对系统的权限进行扩展。本文就我在实际项目过程中所遇到的各种权限方面的问题,看我如何通过扩展Django的权限系统,实现灵活的权限管理。

拿个上周的Jira说明一下,学长遇到的其实是针对不同组,来显示不同的数据内容,我是用的DRF-去重写get_queryset方法实现,因为我想到这个方法最为简单,效果也十分不错,时间就给三五天,于是我就这么干了,下面不细说我的项目,毕竟签了商汤的保密协议~但是学长找了一个很好拓展权限的例子,比我写的好,以此来分享给大家✌

二、扩展Django的权限系统

2.1 对象级权限

在开发过程中,遇到最多的问题应该就是对象级权限的问题了。Django内置的权限授权是针对Model进行的授权,每个Model默认都会生成三个权限:add、change、delete。如一旦授予某用户Change权限,那么该用户就拥有了该Model所有对象的Change权限,但有些时候我们希望仅允许用户对该Model下的指定对象进行操作。

当前学长当前这个项目内容比较乱,所有的项目数据报告都归类到了一起,其实这么做看似简单,后期处理十分麻烦。leader突然要让你分开,自己组看自己组的报告...我自己做个数据的过滤,也就是上方说的重写get_queryset方法,因为接口我用的DRF,所以修改起来也还算比较整齐。分享部分代码供参考,就是先通过用户判断组别,然后在进行数据过滤筛选

所谓对象级权限,即针对的Model对象实例进行授权操作,使用我们可以精确的对单个对象进行权限的控制,以实现更细精度的权限控制。

注:此节内容主要分享了下我自己的重写,没有太多实质性的东西,但很多业务上的权限需求是需要结合多种权限机制实现的,在此列出是为方便后文的展开,让各位先有个概念上的认识。其实DRF也有一套permission_class可以用的,有兴趣同学也可以了解了解。

2.2 系统组的实现

什么是系统组?Django默认的权限系统中只有组的概念,没有什么系统组啊?其实系统组只是我们为了区别与现有组的一种称呼而已,你可以给它起一个更酷的名字。那么如何理解系统组呢?我们知道Django里的组都需要我们进行硬性的绑定才会生效的,比如你创建一个“编辑”的组,那么你需要给编辑这个组授予相应内容的编辑权限,同时你需要将相应的用户分配至该“编辑”组,那么用户才会真正的拥有了编辑应有的权限,这在大多数情况下是没有问题的,但这种硬性的绑定极大的限制了系统的灵活性,因为很多时候系统是需要根据运行时的环境来决定用户应该属于哪些组的。结合上文对象级权限中新闻发布的例子,如果使用对象级权限的方式来实现的话,我们就必需在用户发布新闻的同时,向对象级权限系统中添加一条权限分配的记录(对象级权限系统是需要独立的数据表来记录用户或组与对象的权限关系),这好像也没什么问题,无非是重写save方法或都添加一条signals就可以应对了,但如果我们希望用户的上级领导也能够修改该信息呢,你可能会说给上级领导也添加一条对象级权限啊,但如果该用户的上级领导变更了呢?如果我们又希望用户同部门的员工允许修改呢?是不是开始变得麻烦了,如果有一种机制来处理这种动态关系,那事情就变得简单多了,而实现这一机制的就是系统组。系统组就是在系统运行期,根据运行环境来决定用户所隶属的组,从而实现灵活的授权机制。

看完上面的解释,好像有点明白了,但具体该怎么实现呢?

首先,大家需要理解的一点是系统组本身并非系统运行时动态创建的,而是在开发阶段根据业务需要创建的,系统组机制只是在运行时由系统决定用户与系统组的关系而已。因而在业务开发阶段,我们就必需确定好业务所需的系统组。当然我们也可以从所有的业务中抽象出一些具备全局通用性的系统组,以下列出我们抽象出来的具备通用性的系统组供大家参考:

      1) Everyone:所有人,无论是用户还是访客,都属于Everyone组。
  2) Anonymous:匿名用户,非认证的用户都属于Anonymous组。
  3) Users:用户,所有认证的用户都属于Users组。
  4) Creator:创建者,针对具体信息的创建者,都属于Creator组。
  5) Owner:所有者,具体信息的拥有者,都属于Owner组。

 

看了上边所列出的系统组,大家是不是感觉好像又更理解了一些呢,做过Windows文件授权的朋友,可能还有点似曾相识的感觉,对,这和Windows系统里的特殊组是一样一样的。除了以上所列出的系统组,我们还可以针对特定的业务创建针对性的系统组,如上例中,允许用户的上级领导修改该用户所发布的信息,那么我们可以创建一个名为“信息所有者的上级领导”这样的系统组。

下面我们参考一份拓展django-admin权限的一个设计代码:

 

# 看下面代码,我们声明了我们所需的系统组的常量,声明为常量是便于代码的调用
SYSTEM_GROUP_EVERYONE = "Everyone" # 所有人
SYSTEM_GROUP_ANONYMOUS = "Anonymous" # 匿名用户
SYSTEM_GROUP_USERS = "Users" # 用户
SYSTEM_GROUP_STAFFS = "Staffs" # 职员
SYSTEM_GROUP_CREATOR = "Creator" # 创建者
SYSTEM_GROUP_OWNER = "Owner" # 所有者

根据上文所述,系统组是在开发阶段就应该确定!!!!否则后期像学长一样被坑成🐕,那么确定后的系统组应该如何在系统中体现呢?其实系统组只是我们定义的一个概念,为了保证Django系统权限调用接口的一至性,它仍然是一个组对象,只是是一个我们赋予了特殊意义的组,我们仍然需要在Django的“组”这个Model中创建相应的系统组对象(当然如果考虑到系统组的特殊性,可以通过一些机制来限制对系统组的修改和删除操作,以保证系统组始终可用,如何限制不是权限系统的核心,在此不做详述)。你可以通过声明一个系统组初始化的方法,并在适当的地方调用他,当然你也可以使用Django提供的初始数据方法来初始系统组。看代码:

 

from django.contrib.auth.models import Group

 

def init_systemgroups():

"""
初始化系统组。
:return:
"""
Group.objects.get_or_create(name=SYSTEM_GROUP_EVERYONE)
Group.objects.get_or_create(name=SYSTEM_GROUP_ANONYMOUS)
Group.objects.get_or_create(name=SYSTEM_GROUP_USERS)
Group.objects.get_or_create(name=SYSTEM_GROUP_STAFFS)
Group.objects.get_or_create(name=SYSTEM_GROUP_CREATOR)
Group.objects.get_or_create(name=SYSTEM_GROUP_OWNER)

好了,现在我们已经有了系统组数据了,接下来我们就需要实现系统组的核心逻辑了,从Django所提供的权限验证接口User.has_perm(perm, obj=None),我们可以看出存在两种情况:

一种是不提供obj参数的情况,我们可以语义化理解为“用户(User)是否拥有perm权限”;一种是提供了obj参数的情况,我们可以语义化理解为“用户(User)是否拥有指定对象(obj)的perm权限”。

同样的,我们系统组也可以分为两种情况:一种是与obj参数无关的,我们可以语义化理解为“用户(User)是否为系统组(system group)的成员”,这包括上面所列的Everyone、Anonymous、Users、Staffs等;一种是与obj参数有关的,我们可以语义化理解为“用户(User)是否为对象(obj)的系统组(system group)的成员”,包括上面所列的Creator、Owner等。依赖于相同的参数,使得我们可以保持与Django一至的验证接口,我们现在要做的是根据这些参数,给系统返回恰当的系统组,先来看看与obj参数无关的情况的代码:

 

def get_user_systemgroups(user):
"""
获取指定用户所属的系统组集合。
:param user: 指定的用户。
:return: set 表示的用户所属的系统组名称集合。
"""
groups = set()
groups.add(SYSTEM_GROUP_EVERYONE)
if user.is_anonymous():
groups.add(SYSTEM_GROUP_ANONYMOUS)
else:
groups.add(SYSTEM_GROUP_USERS)
if user.is_staff:
groups.add(SYSTEM_GROUP_STAFFS)

return groups

OK,是不是很简单,我们只是对User进行简单的验证,就可以获得User有关的系统组了,而第二种与obj参数有关的情况就比较复杂了,比如Creator组,我们必需要获取对象的创建者,我们才能与User进行比较,从而验证User是否为obj的创建者,然而Creator组是我们抽象出来的全局通用的组,意味着我们的Model需要提供一至的方法来获取对象的创建者,这时我们需要定义一个接口(Python没有提供接口的概念,我们只是通过抽象的方法来模拟)来实现:

 

class CreatorMixin(object):
"""
实现创建者的 Model 基类。
"""
def get_creator(self):
"""
获取对象的创建者,子类重写该方法实现创建者对象的获取。
:return: 当前对象的创建者。
"""
return None

def set_creator(self, user):
"""
设置对象的创建者,子类重写该方法实现创建者对象的设置。
:param creator: 要设置为创建者的User对象。
:return:
"""
pass

 

class OwnerMixin(object):
"""
实现所有者的 Model 基类。
"""
def get_owner(self):
"""
获取对象的所有者,子类重写该方法实现所有者对象的获取。
:return: 当前对象的所有者。
"""
return None

def set_owner(self, user):
"""
设置对象的所有者,子类重写该方法实现所有者对象的设置。
:param owner: 要设置为所有者的User对象。
:return:
"""
pass

现在再来看看第二种情况的实现:

 

def get_user_systemgroups_for_obj(user, obj):
"""
获取指定用户相对于指定的对象所属的系统组集合。
:param user: 指定的用户。
:param obj: 相对于指定的对象。
:return: set 表示的用户所属的系统组名称集合。
"""
groups = set()
if isinstance(obj, CreatorMixin) and obj.get_creator() == user:
groups.add(SYSTEM_GROUP_CREATOR)
if isinstance(obj, OwnerMixin) and obj.get_owner() == user:
groups.add(SYSTEM_GROUP_OWNER)
return groups

现在,我们为了保证系统组的扩展性,我们需要定义一套规则,使得你可以在你自己的应用中,扩展实现自己业务所需要的系统组,我们约定在你的应用中应该存在一个模块,模块中应该包含有以上声明的get_user_systemgroups(user)和get_user_systemgroups_for_obj(user, obj)两个方法,同时你需要在项目的settings.py文件中,告诉系统你的系统组实现的模块路径,类似如下:

 

# 自定义系统组实现
SYSTEM_GROUP_IMPLEMENTERS = ['systemgroups.systemgroups', '你自己实现的系统组的路径'…]

同时,我们需要提供一个方法来根据上面规则,依次获取所有应用中的用户所属的系统组集合,代码如下:

 

def get_user_systemgroups(user):
"""
从所有应用中获取指定用户所属的系统组集合。
:param user: 指定的用户。
:return: set 表示的用户所属的系统组名称集合。
"""
imps = SYSTEM_GROUP_IMPLEMENTERS
groups = set()
if not imps:
return groups for imp in imps:
imp = importlib.import_module(imp)
if hasattr(imp, "get_user_systemgroups"):
groups.update(imp.get_user_systemgroups(user))
return groups

 

def get_user_systemgroups_for_obj(user, obj):
"""
从所有应用中获取指定用户相对于指定的对象所属的系统组集合。
:param user: 指定的用户。
:param obj: 相对于指定的对象。
:return: set 表示的用户所属的系统组名称集合。
"""
imps = SYSTEM_GROUP_IMPLEMENTERS
groups = set()
if not imps:
return groups for imp in imps:
imp = importlib.import_module(imp)
if hasattr(imp, "get_user_systemgroups_for_obj"):
groups.update(imp.get_user_systemgroups_for_obj(user, obj))
return groups

最后,我们来实现我们的认证后端:

 

def get_group_permissions(name):
"""
获取指定名称的组所拥有的权限集合。
:param name: 组的名称。
:return: 权限集合。
"""
perms = Permission.objects.filter(group__name = name)
perms = perms.values_list('content_type__app_label', 'codename').order_by()
return set(["%s.%s" % (ct, name) for ct, name in perms])def get_groups_permissions(names):
"""
获取指定名称的组所拥有的权限集合。
:param names: 组的名称集合。
:return: 权限集合。
"""
perms = set()
for name in names:
perms.update(get_group_permissions(name))
return permsclass SystemGroupBackend(object):
def authenticate(self, username=None, password=None, **kwargs):
return None

def has_perm(self, user_obj, perm, obj=None):
return perm in self.get_all_permissions(user_obj, obj)

def get_all_permissions(self, user_obj, obj=None):
perms = self.get_group_permissions(user_obj, obj)
return perms def get_group_permissions(self, user_obj, obj=None):
result_perms = set()

groups = get_user_systemgroups(user_obj)
perms = get_groups_permissions(groups)
result_perms.update(perms)

if obj is None:
return result_perms

groups = get_user_systemgroups_for_obj(user_obj, obj)
perms = get_groups_permissions(groups)
result_perms.update(perms)

return result_perms

至此,我们完整的实现了系统组的机制,以上代码因篇幅原因删减了部分与主逻辑关系不大的代码,如权限的缓存等,以便于阅读和理解,完整的代码请参考项目本著的GitHub:https://github.com/Kidwind/django-systemgroups。

2.3 权限映射

权限映射又是什么呢?要回答这个问题,我们还是以上文中的新闻发布的例子来展开,我们的新闻应该是根据性质进行分类的,比如实事新闻、财经新闻、体育新闻等等,这时我们就形成了新闻类别(InfoCategory)和新闻(Info)这两个一对多关系的Model,随着工作的细分,我们需要将不同的分类授权不同的部门来进行管理这时你想到的是什么?对,就是上文中所提到的对象级权限,我们给新闻类别(InfoCategory)创建一个用于控制新闻类别下的新闻的修改权限,名为change_info_by_category,通过针对新闻类别(InfoCategory)进行对象级的change_info_by_category授权,如果此时我们要进行某篇新闻的修改权限验证,我们需要对新闻所在栏目进行change_info_by_category的权限验证,像这样user.has_perm(‘app_label. change_info_by_category’, obj=info.category),有什么问题吗?似乎也没什么问题,但我们细细分析一下,我们原本对新闻(Info)进行修改的权限验证方法user.has_perm(‘app_label.change_info’, obj=info),需要人为的转换为上面的权限验证,相应的Django提供的Admin我们需要重写相应的方法来修改权限验证的逻辑,如果新闻(Info)本身还提供对象级的权限检测,我们的逻辑就需要改为要对两个方法都进行验证,还有更多复杂的情况,情况一变,我们就需要重写我们的权限验证逻辑吗,很麻烦不是吗,如果有一种方法能够实现上述权限验证的自动转换,能够保证我们的权限调用方法不变,那事情就简单多了,而这一方法,就是我们所说的权限映射。简而言之,权限映射就是将用户对当前对象所执行的权限验证转换为用户对另一个对象的另一个权限进行验证的过程

看下图:

 

好了,分析到这里,相信大家对权限映射的作用有了大概的认识,下面我们来对代码实现做简单的分析和了解。同系统组一样,我们的Model需要提供一至的方法来根据当前的权限验证参数,获取映射后的权限验证参数,我们定义接口如下:

 

class PermMappableMixin(object):
"""
实现权限映射的 Model 基类。
"""
@classmethod
def mapping_permission(cls, perm, obj=None):
"""
根据当前的权限验证参数,获取映射后的权限验证参数。(此类方法仅为标记方法,子类应实现相应的方法)
:param perm: 当前检测的权限。
:param obj: 当前进行检测权限的对象。
:return: 返回值包含两个参数:第一个参数为映射后的权限;第二个参数为对应映射后的对象,其应为映射后权限所对应的 Model 的实例。
"""
return None, None

接下来,我们只需要实现我们的认证后端就可以了:

 

from django.contrib.contenttypes.models import ContentTypeclass PermMappingBackend(object):
def authenticate(self, username=None, password=None, **kwargs):
return None

def has_perm(self, user_obj, perm, obj=None):
app_label, codename = perm.split('.')
content_types = ContentType.objects.filter(
app_label = app_label,
permission__codename = codename) # 根据权限获取其对应的ContentType实例。
for content_type in content_types:
model_class = content_type.model_class() # 根据 ContentType 实例获取对应的 Model 类
if issubclass(model_class, PermMappableMixin):
mapped_perm, mapped_obj = model_class.mapping_permission(perm, obj = obj)
if mapped_perm and user_obj.has_perm(mapped_perm, obj=mapped_obj):
return True
return False

是不是很简单,接下来看看我们新闻例子的代码:

 

from django.utils.translation import ugettext as _from django.db import modelsclass InfoCategory(models.Model):
name = models.CharField(max_length=128, verbose_name=_('分类名称'))

class Meta:
permissions = (
("add_info_by_category", _("允许添加分类信息")),
("change_info_by_category", _("允许修改分类信息")),
("delete_info_by_category", _("允许删除分类信息")),
)class Info(PermMappableMixin, models.Model):
category = models.ForeignKey(InfoCategory, verbose_name=_("所属分类"))
title = models.CharField(max_length=256, verbose_name=_('标题'))

@classmethod
def mapping_permission(cls, perm, obj=None):
mapped_perm = None
mapped_obj = None

if perm == "permmapping.add_info":
mapped_perm = "permmapping.add_info_by_category"
elif perm == "permmapping.change_info":
mapped_perm = "permmapping.change_info_by_category"
elif perm == "permmapping.delete_info":
mapped_perm = "permmapping.delete_info_by_category"

if isinstance(obj, cls):
mapped_obj = obj.category return mapped_perm, mapped_obj

三、写在最后

Django系统提供了一套灵活的认证系统,使得我们可以通过扩展其实现灵活的权限控制策略,本文结合我在项目过程中的实践经验以及网络大佬的代码分享,为大家展示了通过对象级权限、系统组的实现、权限映射来解决项目中所遇到的几种权限问题,同时实践中也可以通过组合这几种权限机制,实现更多更为复杂的权限策略。其它类的开发语言,也可以借鉴Django及上文所提到的几种权限系统的扩展的思路,实现各自平台的权限系统

好了,先到这里了,如果大家在实践中有什么问题,可以给我留言,Bye~

 


 

-以上简单描述希望对你有所帮助。共勉-

以下为分享的宝藏内容

 


 

 

我认为资料的价值在于能用、好用,不是满足人的占有欲和获得感。所以,也请各位擦亮双眼,提高标准。得到的同时记得他的价值所在,收获的同时,也请做好择优标准。BTW,学长做的不好的地方,欢迎你们提出来,又或者如果屏幕前的你将更好的资源拿出分享,那真的十分优秀,也希望各位能无私互助。获取资料不强制转发。

希望学长分享的内容对你我都有帮助💪

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455776.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux使用circos

1.在conda中安装bioconda conda install -c bioconda circos -y # 测试是否所有的module都安装好了 circos -module # 所有都显示OK则成功 ok 0.39 Font::TTF::Font ok 2.68 GD ok 0.2 GD::Polyline ... .... 2.检查模块是否齐全 circos -module 3.下…

【Mysql】数据库主从搭建-基于docker

后台可回复【1024】即可获取相关宝藏内容分享 :) 为什么基于Docker搭建? 资源有限 虚拟机搭建对机器配置有要求,并且安装mysql步骤繁琐 一台机器上可以运行多个Docker容器 Docker容器之间相互独立,有独立ip,互不冲突…

java创建对象new后面为啥可以传入参数_你有认真了解过自己的“Java对象”吗?渣男...

对象在 JVM 中是怎么存储的对象头里有什么?作为一名 Javaer,生活中的我们可能暂时没有对象,但是工作中每天都会创建大量的 Java 对象,你有试着去了解下自己的“对象”吗?我们从四个方面重新认识下自己的“对象”创建对…

【技术+某度面经】Jenkins 内容+百度面经分享

后台可回复【1024】即可获取相关宝藏内容分享 :) Q1: Jenkins是什么?? A:Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。 今天文章分为两部分 :) PART1 Jenkins技术分享 / PART2 关…

xcode多工程联编 - 详细教程

2019独角兽企业重金招聘Python工程师标准>>> 一、创建workspace (MyProject)放入MyProject文件夹内 二、先 打开workspace 创建app1工程 点击next之后注意选择 workspace 同理创建app2 或者更多的工程 完成之后的工作 重新打开workspace的样子 三、使用pod 库 首先…

实数是不是python数据类型_python 基本数据类型

一、数据类型及操作 #整数类型,和数学中整数的一样,可正可负 *十进制:210 *二进制:以0B或者0b开头:0b1010 *八进制:以0O或者0o开头:0o123 *十六进制:以0x或者0X开头:0x9a…

Principle of Computing (Python)学习笔记(7) DFS Search + Tic Tac Toe use MiniMax Stratedy

1. Trees Tree is a recursive structure. 1.1 math nodes https://class.coursera.org/principlescomputing-001/wiki/view?pagetrees 1.2 CODE无parent域的树 http://www.codeskulptor.org/#poc_tree.py class Tree:"""Recursive definition for tree…

C#线程篇---Task(任务)和线程池不得不说的秘密

我们要知道的是,QueueUserWorkItem这个技术存在许多限制。其中最大的问题是没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成是获得一个返回值,这些问题使得我们都不敢启用这个技术。 Microsoft为了克服这些限制&…

【百度面试】闸机测试场景

面试被问到这一题思路想法: 自己找了相关内容充实自我。内容分享如下: 随着人脸识别技术的成熟,闸机行业大量应用人脸识别算法,只因现今的人脸识别算法也已经能够保证识别率、识别速度、误识率和拒识率等各项指标的优异性&#x…

前后端分离项目如何部署_前后端分离项目,如何解决跨域问题?

跨域资源共享(CORS)是前后端分离项目很常见的问题,本文主要介绍当SpringBoot应用整合SpringSecurity以后如何解决该问题。01 什么是跨域问题?CORS全称Cross-Origin Resource Sharing,意为跨域资源共享。当一个资源去访问另一个不同域名或者同…

使用模板引擎artTemplate的几个问题总结

一、Template not found 有的时候模板写的并没有问题&#xff0c;可就是找不到。这时候可能是<script>加载顺序问题&#xff0c;模板渲染在模板加载完成之前先执行了&#xff0c;调整<script>的顺序。 二、模板中将字符串转化成数字 利用html中的表单来转化&#x…

时间戳问题汇总

大家好 我刚接触流媒体不久&#xff0c; 现在遇到一个非常奇怪的问题&#xff0c;向各位大侠请假&#xff0c;请你们指点。 问题是这样的 用一个 VLC(流媒体客户端) 去请求流媒体服务器上的数据&#xff0c; 但是获得的数据播放速度明显快于1倍速&#xff0c;大概是 timest…

nginx反向代理配置 多个_实例分享:Nginx学习之反向代理WebSocket配置实例

写在开始去年&#xff0c;做过一款竞赛打分的APP。具体需求&#xff0c;同组教师之间可以相互通信&#xff0c;及时通知同组人员&#xff0c;其他组员做了那些操作(当然&#xff0c;这只是针对特定操作)。实现方案采用目前比较成熟的WebSocket技术&#xff0c;WebSocket协议为创…

性能测试总结(一)---基础理论篇

随着软件行业的快速发展&#xff0c;现代的软件系统越来越复杂&#xff0c;功能越来越多&#xff0c;测试人员除了需要保证基本的功能测试质量&#xff0c;性能也随越来越受到人们的关注。但是一提到性能测试&#xff0c;很多人就直接连想到Loadrunner。认为LR就等于性能测试&a…

Makefile 7——自动生成依赖关系 三颗星

后面会介绍gcc获得源文件依赖的方法&#xff0c;gcc这个功能就是为make而存在的。我们采用gcc的-MM选项结合sed命令。使用sed进行替换的目的是为了在目标名前加上“objs/”前缀。gcc的-E选项&#xff0c;预处理。在生成依赖关系时&#xff0c;其实并不需要gcc编译源文件&#x…

集合添加元素python_Python 集合(Set)

Python 集合&#xff08;Set&#xff09; 在本文中&#xff0c;您将学习关于Python集的所有内容;如何创建它们、添加或删除其中的元素&#xff0c;以及在Python中对集合执行的所有操作。 Python中的集合是什么&#xff1f; 集合是项目的无序集合。每个元素都是唯一的&#xff0…

【分享】 codeReview 的重要性

研发都知道代码 Review 的重要性&#xff0c;在代码 Review 也越来越受大家重视&#xff0c;我参与了大量的代码 Review&#xff0c;明显地感受到有效的代码 Review 不但能提高代码的质量&#xff0c;更能促进团队沟通协作&#xff0c;建立更高的工程质量标准&#xff0c;无论对…

线程02

2019独角兽企业重金招聘Python工程师标准>>> 线程中有几个方法需要我们区分 1 sleep方法是表示线程执行到这的时候只是暂时处于“睡眠”状态&#xff0c;在这种状态下线程是不会释放CPU资源的&#xff0c;当到达休眠时间后&#xff0c;线程继续“起来”干活。当线程…

@postconstruct注解方法没有执行_把对象的创建交给spring来管理(注解IOC)

自动按照类型注入/** * 账户的业务层实现类 * * 曾经XML的配置&#xff1a; * <bean id"accountService" class"com.itheima.service.impl.AccountServiceImpl" * scope"" init-method"" destroy-method""> * <pro…