36. 权限系统(上)

系统权限设计架构:

权限系统的终极目的是判断在某情景下谁能对某物做什么或者不能做什么,可以看出有三个基本要素:操作者、被操作者、操作环境(或者叫上下文),对应的权限系统就好像一个警卫,房间里面是被操作者,操作者要进入房间去操作被操作者,此时门卫会根据情况来做判断,允许就放其进入,反之拒绝,一旦进入了,警卫就不管操作者具体要做什么了,如果要进行更加细粒度的权限检查那么就把被操作者拆分成更小的部分,分别放入不同房间,然后每个房间再设置警卫。

 

drupal的权限检查从执行时间角度(也可叫做流程先后角度)可以分为两大块内容:

入站检查:作用在路由上,当一个访问请求到达后,这里负责指示系统允许进入还是拒绝进入

业务逻辑检查:作用在具体业务逻辑上,是一种细部检查,在业务程序逻辑中判断某用户是否有权执行某些事情。

这个世界是多角度的,以上分类方法从开发的角度来看,并没有严格的界限,比如入站检查可能也会涉及到业务逻辑检查,读者不必太纠结,了解即可,下面主要按时间间角度来讲解,本主题分为上下两节,上节讲述入站检查,下节讲述业务逻辑检查,最终我们会理解drupal的具体实现,对所有角度一通百通。

入站检查:

入站检查是作用在路由上面的,路由是进入系统的必经之地,进行路由处理后我们就知道程序流程将去向何处,在去之前drupal执行权限检查,系统路由是在派发kernel.request事件后由路由侦听器服务处理:

侦听器服务id:router_listener

类:Symfony\Component\HttpKernel\EventListener\RouterListener

处理方法:onKernelRequest(GetResponseEvent $event)

内部它会调用路由服务来处理:路由服务的服务id:router

类:Drupal\Core\Routing\AccessAwareRouter

在该服务中方法:checkAccess(Request $request);就是入站检查逻辑的入口了,由访问管理器执行检查,我们先需要知道几个访问管理器调用的组件:

用户账户对象Account

Account对象代表一个用户账户,提供一个用户的基本信息,如ID、角色、权限、语言、时区等等,在Drupal中用户账户对象需要实现此接口:

Drupal\Core\Session\ AccountInterface

可从该接口中知道我们可以使用哪些信息,系统默认使用的账户对象为:

Drupal\Core\Session\ UserSession

该账户对象是在系统派发kernel.request事件时由订阅器authentication_subscriber产生,该订阅器的类为:

Drupal\Core\EventSubscriber\AuthenticationSubscriber

在该订阅器中利用认证模块产生账户对象,系统实际最终产生账户对象的核心代码位于:

Drupal\user\Authentication\Provider\Cookie的getUserFromSession(SessionInterface $session)方法中

(关于认证模块,它的代码位于\core\lib\Drupal\Core\Authentication中,详细请查阅本系列认证主题:

http://blog.csdn.net/u011474028/article/details/53064945

订阅器将产生的账户对象注入当前用户服务current_user中,该服务如下:

服务id:current_user

类:Drupal\Core\Session\AccountProxy

获取方法:\Drupal::currentUser(); 或$this->container->get('current_user');

 

以上过程在默认实现中可以简述为系统先从会话session中取到用户ID,再到数据库查询用户数据(使用到了两张数据表users_field_data和user__roles),根据查询到的信息产生用户账户对象,最后将这个对象注入当前用户服务里面,在系统后续流程中就可以使用该服务得到当前用户账户对象了

可以看出整个过程是基于会话session中的用户ID的,会话数据保存在服务器中,需要有一个会话ID来找到它,要该用户ID真实合法需要有一个真实合法的sessionID,session负责OSI网络模型的会话层,它解决了http协议无状态问题,为对端用户生成一个长的识别标识ID,以“SESSIONID=值”的形式,在drupal中真实的标识如下:

SESS30c25d0f8ffe1bbe9ef7049663638486=FwIiZPyPVQcAvpsvJD_1pVNKj62m2k5w3em19oWi5wg

该信息以cookie的方式保存在客户端浏览器中,访问的时候会向服务器发送该信息,通过这个字符串我们可以看出它能表达的数值大小,这是一个天文数值,若想通过猜测的方式冒用身份几乎是不可能的,该会话id由服务器生成管理,本质上就是授予客户端与服务器的交互凭证,所以该会话ID如果被第三方盗取,那么第三方也就可以冒用用户身份和服务器通讯了。

 

如果在cookie中不能提供合法有效的session ID系统不会形成会话数据,也就不会有以上形成用户账户对象的过程,反之有了账户对象就认为用户可信,可以根据它提供的信息去判断在系统中哪些动作可以执行哪些不可以,判断逻辑就是访问管理的事情了。

 

有了用户账户对象是否意味着已经登录?不一定,在默认实现中一定会有一个账户对象,对象中的uid为0表示匿名用户,相当于访客处于未登录状态,如果uid大于0,表示访客已经登录了,所以只有账户对象存在且uid大于0才表示已经登录,能否像淘宝一样在没有登录时,页面也能显示出用户名呢?很简单,设置一个保存用户名的cookie,控制器获取即可,这里需要注意在cookie里面试图设置uid去登录是没有用的,系统使用的是session里面的uid而不是cookie里面的,能冒充登录只有在cookie里面提供正确的会话id,上文已经说了会话id几乎不可能猜出来。

检查结果表示AccessResult

权限检查系统的目的就是要给出一个依据,让系统根据这个依据去允许或拒绝某些执行,这个依据在drupal中叫做AccessResult,可翻译为访问控制检查结果,或权控凭据,在系统中以如下抽象类来表示:

Drupal\Core\Access\AccessResult

我们来看一看这个权控凭据应该具备什么特性:

 

在进行权限检查时,得到这个权控凭据是需要计算的,如果每个访问都去计算那么系统性能会下降,所以需要将这个凭据缓存起来,可是如果进行缓存那么在权限配置变化时怎么办呢?不用担心,这些问题其实已经被缓存三要素(上下文、标签、可缓存时间)给解决了,所以缓存是可行的而且是必要的,那么由此可见权控凭据需要具备可缓存性,在drupal中就体现在它需要实现可缓存依赖接口CacheableDependencyInterface,这是它的第一个特征:可缓存特征。

 

对一个权限的检查,只可能做出三种决策:明确允许、明确禁止、和自己无关或无法判断而放弃检查保持中立,那么权控凭据就对应三种状态:允许Allowed、禁止Forbidden、中立Neutral,这是它的第二个特征:状态特征,在drupal系统中实现了三个类来分别表示这三种状态:

Drupal\Core\Access\AccessResultAllowed:允许

Drupal\Core\Access\ AccessResultForbidden:禁止

Drupal\Core\Access\ AccessResultNeutral:中立

它们都继承自抽象母类:

Drupal\Core\Access\AccessResult

 

实际中一项权限检查可能经过多个关卡(一个关卡对应一个检查器,见后文),每个关卡都会给出一个权控凭据,但我们只关心最终结果,也就是只需要一个权控凭据来做决定,那么就需要将每个关卡的权控凭据进行合并,最终只得到一个,那么怎么合并呢?这是要视业务场景而定的,通常有以下几种情况:只要有一个允许就允许、只要有一个不允许就不允许、中立可视为允许或不允许,更加复杂的合并操作可以使用逻辑操作符进行各种组合完成,由于业务场景的多样性系统不可能预先定义出所有的合并操作,但却可以定义最常见的合并,见下。

系统作为一个整体是内聚的,之所以要进行权限检查也就暗示着默认已将外界视作不安全的,这也符合自然规律,就好比一个生物保持警惕心可减少受到伤害,如果什么也不怕乐观的以为这个世界是善意的那么它可能就活不下去,所以就产生了最常见的权控凭据合并规则,系统默认提供了这种规则的实现:

1:只要有一个禁止那么结果就禁止,就好比某关卡程序在说:你若放它进来我所保护的内容就会受到破坏,那么系统就应该拒绝,禁止的优先级是最高的,它有传染性,不管什么与它合并结果都为禁止。

2:都为允许即允许。

3:中立和禁止合并前面已经说了为禁止,但和允许合并:分两种情况:结果允许(宽松检查)、结果还是中立(严格检查)

 

这里需要注意:多个权控凭据的合并和只有两个权控凭据的合并规则是没有区别的,能合并两个就能推及到合并多个,为了简单起见以两个作为研究对象。

根据上面中立合并的两种情况系统定义了两个合并方法:

为使用方便起见它们在权控凭据的抽象母类Drupal\Core\Access\AccessResult中实现,见如下:

andIf(AccessResultInterface $other):严格合并检查,规则如下:

合并的两方只要其中一方是禁止,结果为禁止

在没有禁止情况下,只要有一方为中立,结果为中立

两个都是允许结果才为允许

可用下表来表示:

|A N F

--+-----

A |A N F

N |N N F

F |F F F

 

orIf(AccessResultInterface $other):宽松检查合并,规则如下:

合并的两方只要其中一方是禁止,结果为禁止

在没有禁止情况下,只要有一方为允许,结果为允许

两个都是中立结果为中立

可用下表来表示:

|A N F

--+-----

A |A A F

N |A N F

F |F F F

严格检查和宽松检查区别在允许和中立合并时的结果上,而禁止的处理结果是相同的,禁止具备传染性,也就是说只要有一个拒绝那么不管多少个合并,结果就是全部拒绝。

这两个合并方法在系统中多处使用,比如实体检查、字段访问检查等等。以上只是他们状态的合并,权控凭据是可缓存的,那么在这种合并逻辑下他们的可缓存属性怎么合并呢:

首先需要明白缓存属性代表的是权控凭据在缓存中的废止条件,废止也就是缓存失效引起重新计算权控凭据。其次合并是没有方向性的,也就是说合并A、B、C三个权控凭据,从A开始合并和从C开始合并,结果应该一样,但为了缓存优化,允许不一样,只要保证权限检查结果正确即可。只要清楚了两个权控凭据的合并情况就能推及到多个,这里以A、B合并为C为列子来说明,下面我们看一下andIf 和orIf这两种合并的情况:

andIf:

结果为禁止的情况:

AB其中一方禁止是结果C为禁止的充要条件,那么只需要一个禁止的缓存属性并入C即可保证禁止结果的有效性,不管另一个是什么状态,并入不是必需的,如果并入也不会影响准确性,但缓存性能降低,因为并入后引起缓存失效的因素增加,只要被并入的那个禁止的缓存属性不变就能保证结果为禁止,而增加的因素引起的失效是不必要的。基于这一点另外一方不应该被并入,所以A+B和B+A的结果可以不相同,这是优化的结果。

结果为允许的情况:

这种情况下AB均必须为允许,只要他们中任何一个缓存属性有变化都可能引起结果变化,所以他们的缓存属性都必须要并入结果中,MaxAge以小的为准

结果为中立的情况:AB任何一个的缓存属性变化都可能导致变为禁止,而导致C的缓存失效,所以都应该并入

orIf:

结果为禁止的情况:和andIf情况相同

结果为允许的情况:AB只要有一个变为禁止,那么就会引起缓存失效,所以它们的缓存属性应当都并入结果

结果为中立的情况:同允许情况一样

可看出andif和orif的缓存合并处理是相同的。

 

在orIf的默认实现中,云客发现是有安全漏洞的,如下:

问题就出在orIf合并中的缓存属性合并上面,假设有两个凭证对象A、B它们都为允许状态:

A的getCacheMaxAge为0,B的为非0,合并结果将以非0为准

A的MaxAge为0,表示不可缓存,可能随时间变化,变为禁止时,在缓存中的合并结果在B的MaxAg范围内是有效的,所以在B不超期时仍然为允许,但实际上A已经变为禁止了。

在用户使用层面体现在已经过期的授权还会继续有效

 

该安全bug云客已经向官方提交

访问管理access_manager

访问管理器是系统执行入站检查的综合管理者,提供给系统一个使用权限检查API的入口:

服务id: access_manager

类:Drupal\Core\Access\AccessManager

获取方法:\Drupal:: accessManager (); 或$this->container->get('access_manager');

使用它执行对请求的检查:

$access_result = \Drupal:: accessManager ()->checkRequest($request, $account, TRUE);

$access_result->isAllowed(); //返回bool,表明是否明确允许,中立算不允许

 

访问管理器实现了以下接口:

Drupal\Core\Access\ AccessManagerInterface

该接口中有三个方法,核心是check方法,其他两个只是便于我们使用,它们最终使用了check方法

check方法的签名如下:

check(RouteMatchInterface $route_match, AccountInterface $account = NULL, Request $request = NULL, $return_as_object = FALSE);

第一个参数为路由匹配器,它是一个访问请求的路由信息表示,入站权限检查是作用在路由信息上面的,路由信息就像一道门,房间里是我们想要保护的内容,权限检查就像警卫

最后一个参数$return_as_object如果为true则返回权控凭据对象,如果是false就返回权控凭据对象的isAllowed()方法结果,这意味着中立的权控凭据将被视为禁止,因为中立的权控凭据对象的isAllowed()方法将返回false,所以当我们需要更详尽信息时应当返回对象。

 

为什么说访问管理器是综合管理者呢?这是因为它本身所做事情是比较少的,在内部它使用检查提供器CheckProvider提供的多个检查器check来执行检查,每个检查器都返回一个权控凭据AccessResult,访问管理器本身只进行权控凭据的合并,这种合并采用严格合并检查,也就是andIf合并,最终向使用者提供一个唯一的权控凭据,实现中该凭据会被保存到请求对象的属性包中,如下:

$request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $access_result);

可以通过$request->attributes->get("_access_result");获取

 

在介绍检查提供器和检查器之前我们需要先认识一下参数解析器。

参数解析器ArgumentsResolver

检查器check实际上是一个回调,它需要一些参数来进行权限判断,这些参数包括:路由对象、未经转换的原始路径变量、经过参数转换后的路径变量对象、定义路由时路径参数的默认值、路由中提供的额外变量、路由匹配器对象、账户对象、请求对象,但检查器往往只需要这些参数中的一部分而已,比如有些检查器就不需要请求对象,检查器将需要的参数定义在检查方法的签名中,那么系统在调用检测器回调时怎么知道应该传递哪些参数呢?为了解决这个问题系统中定义了参数解析器ArgumentsResolver。

实现中采用参数解析器工厂来生成参数解析器,工厂的作用是预先将这些参数全部传递给即将要使用的参数解析器,工厂是容器中的一个服务,id是:access_arguments_resolver_factory

类:Drupal\Core\Access\AccessArgumentsResolverFactory

它返回的参数解析器对象的类定义为:

Drupal\Component\Utility\ArgumentsResolver

该参数解析器利用php的反射机制判断检查器回调使用什么参数,这允许检查器回调利用参数类型暗示或者参数名来指定自己需要的参数,其他参数将不被传入,在筛选以上提到的那些参数时,有一个优先顺序,在满足类型暗示或参数名相同的情况下,优先级依次如下,1为最高优先:

1经过参数转换后的路径变量对象

2路由对象、路由匹配器、账户对象、请求对象

3未经转换的原始路径变量

4检查器回调本身定义时参数的默认值

详见:Drupal\Component\Utility\ArgumentsResolver:: getArgument(\ReflectionParameter $parameter);

检查器Check

在访问管理器中已经讲到了实际的检查是由检查提供器CheckProvider提供的检查器check来进行的,每个检查器都好比一位警卫,实际上检查经常不止一个警卫,可能有许多个,他们各自检查一项内容,在系统中每一个需要关注被检查权限的模块都可以派出一个警卫,这些警卫由检查提供器统一管理,检查提供器就好比一个安保公司而不是单个具体的警卫人员。为叙述方便后文我们统一使用名词:CheckProvider检查提供器、Check检查器。

在drupal8中所有的检查器都需要实现该接口:

Drupal\Core\Routing\Access\AccessInterface

检查器本质上是一个回调,在参数解析器一节已经讲明它可以接受不同的参数,所以并不存在一个相同的对外接口,所以该接口被设计为空接口,只是起到一个标志而已,在drupal9中它将被移除,关于这点官方已经做了说明:

请见:https://www.drupal.org/node/2266817

目前在drupal8中所有检查器定义依然需要实现该接口,系统中默认定义了二十多个检查器,如需查看请按本系列依赖注入主题里面提供的方法导出容器定义文件,查看私有服务:access_manager.check_provider即可,下面展示其中具体的一个检查器:

Array

(

[0] => access_check.csrf

[1] => access

[2] => Array([0] => _csrf_token)

[3] => 1

)

一个检查器有四项数据,对应上面的例子依次为:检查器在容器中的服务id、执行检查的方法名、检查标识(不明白没关系,见下文)、本检查器是否需要访问请求对象。

来看一下模块如何自定义检查器就明白这四项数据代表的意思了,在D8系统中所有的检查器都是容器中的服务,所以我们需要以定义服务的方式去定义检查器,如何进行服务定义请看本系列相关主题,在此不再细述,且检查器服务均需要实现前文提到的Drupal\Core\Routing\Access\AccessInterface接口,这里展示一个定义例子:(见辅助内容区)

按照最佳实践,服务名以access_check.作为前缀,类名以AccessCheck作为后缀,但不是强制的

必须给出“access_check”标签名,标签里面其他参数是可选的,method表示调用本检查服务时执行的方法名,省略时默认为access,needs_incoming_request代表本检查是否需要请求对象

access_check.yunke:
    class: Drupal\moduleName\Access\YunkeAccessCheck
    tags:
      - { name: access_check, applies_to: _csrf_token, method: access, needs_incoming_request: TRUE }   

 

applies_to选项的值就是上文提到的检查标识,表示如果在路由定义时requirements项中包含了本选项定义的检查标识子项,那么该路由需要进行本检查,比如如下路由定义:(见辅助内容区)

在该路由定义中requirements项有一个_csrf_token子项,那么对此路由的检查将运用系统中所有具备applies_to: _csrf_token定义的检查器,而不管_csrf_token子项的值如何,它的值往往将传递给检查器;

comment.approve:
  path: '/comment/{comment}/approve'
  defaults:
    _title: 'Approve'
    _controller: '\Drupal\comment\Controller\CommentController::commentApprove'
    entity_type: 'comment'
  requirements:
    _entity_access: 'comment.approve'
    _csrf_token: 'TRUE'
    comment: \d+

 

我们定义路由时常用的约束项_permission就是applies_to的一种值,运用该约束的路由会使用以下检查器:(见辅助内容区)

这个检查器服务由用户模块user定义,它将运用到所有指定了_permission约束的路由

access_check.permission:
    class: Drupal\user\Access\PermissionAccessCheck
    tags:
      - { name: access_check, applies_to: _permission }

 

检查器的applies_to选项的值为了叙述简单,云客将其称为检查标识(它是路由定义中约束项数组的键名,不是键值,约束数组除检查标识项外还包括其他内容),applies_to选项可以指定多个值,也就是可以指定多个检查标识,要指定多个检查标识指定多条tags属性即可,如上面的例子指定多个检查标识将如下定义:(见辅助内容区)

method和needs_incoming_request以最后一条为准,这样就指定了两个检查标识,该设计其实可算是一个bug,但drupal8目前就是如此,若你想使用以下定义将出错:

 

- { name: access_check, applies_to: [_csrf_token ,_permission], method: access, needs_incoming_request: TRUE }

这是收集检查器的编译器造成的,后文将会提到

access_check.yunke:
    class: Drupal\moduleName\Access\YunkeAccessCheck
    tags:
      - { name: access_check, applies_to: _csrf_token, method: access, needs_incoming_request: TRUE }
      - { name: access_check, applies_to: _permission, method: access, needs_incoming_request: TRUE }

 

检查标识是联系检查器和路由的纽带,可以自定义,一旦定义后,在路由定义中指定它作为requirements键的子键就可以使用该检查标识对应的检查器了(不管该子键的值为何均会使用);检查标识项的值保存在路由对象中,在检查器里面可以获取到,检查标识不能和系统中已定义的相冲突,系统中已经默认定义了以下检查标识:

_access

_entity_access

_entity_create_access

_entity_create_any_access

_access_theme

_custom_access

_csrf_token

_access_contact_personal_tab

_field_ui_view_mode_access

_field_ui_form_mode_access

_access_node_revision

_node_add_access

_node_preview_access

_access_quickedit_entity_field

_access_system_cron

_access_system_update

_access_update_manager

_permission

_access_user_register

_role

_user_is_logged_in

他们通常以下划线作为前缀,但这不是必须的,只作为最佳实践,推荐以下划线开始,检查标识是在定义检查器时在applies_to中定义的,一个检查器可以定义多个检查标识,方法见上,但为了设计上功能分离检查器往往只定义一个检查标识。每个路由定义也可以指定(注意不是定义)多个检查标识,但至少有一个检查标识(指定_access_checks值的情况除外,见下),否则就会出现没有检查器,这种情况下系统判定为拒绝访问,所以通常我们都会使用_permission检查标识,它的值是当前用户需要具备的权限,对应的检查器是服务:access_check.permission

除了运用检查标识将检查器和路由联系起来外,检查器还有另外一种方法来做到这一点,该方法不需要在检查器的服务定义中指定检查标识,检查器只需要实现Drupal\Core\Access\ AccessCheckInterface接口即可,该接口只有一个方法:

applies(Route $route);

它返回bool值以表明针对某路由是否需要运用本检查器,起到了和检查标识相同的作用,但灵活强大许多。

检查提供器CheckProvider

系统中定义了许多检查器,它们被检查提供器统一管理,检查提供器是一个私有的容器服务,无法从容器中直接get,它在core.services.yml文件中定义:

服务id:access_manager.check_provider

类:Drupal\Core\Access\CheckProvider

 

在容器编译阶段会收集系统中所有的检查器注入到它里面,该工作由以下编译器完成:

Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass

在这个编译器里面可以看出前文提到的指定多个检查标识的bug问题

 

检查提供器包含了所有的检查器,在进行检查时实例化需要的检查器,此外它还根据检查标识或者调用检查器的applies方法来判断一个路由需要使用哪些检查器,但该工作不是在权限检查时进行的,而是在路由建立时。

当一个模块启用时,系统会检查是否有路由定义,如果有将会编译路由并保存到数据库,以后只从数据库查找路由,在该过程中检查提供器会找出该路由需要使用哪些检查器,并把这些检查器的服务id保存到路由options定义的_access_checks子键中,在以后的运行中做检查时从该选项子键取值就知道需要运行哪些检查器了,详见:

Drupal\Core\Routing\ RouteBuilder:: rebuild()

路由选项options属性的_access_checks属性通常作为系统内部使用,在路由定义中有检查标识的情况下会覆盖掉用户自己定义的值,如需自定义则要保证在约束中不出现检查标识项。

 

在保存路由对象时添加检查器,这样的设计一旦系统新增检查器,那么就有个问题需要处理,如果新增的是使用检查标识的检查器没什么问题,但如果是使用实现Drupal\Core\Access\ AccessCheckInterface接口的检查器呢?就需要更新保存的路由对象了,该问题的处理见模块安装主题。

常用检查器:

drupal在标准安装下默认提供了二十二个检查器,下面介绍一些常用的检查器:

 

默认检查器access_check.default

类:Drupal\Core\Access\DefaultAccessCheck

非常简单的检查器,在路由定义中若没有提供检查标识,那么可以使用它做最简单的权限控制:

requirements:

_access: true

检查标识_access的值是bool,或为空,直接代表允许、拒绝、中立

 

自定义回调检查器access_check.custom

检查标识:_custom_access,方法:access,无需请求对象

类:Drupal\Core\Access\CustomAccessCheck

在该检查器中可以自定义一个回调来作为检查器,这带来极大方便,定义方法和路由控制器方法相同,但回调需要返回检查凭据对象

 

用户权限检查器access_check.permission

检查标识: _permission,方法:access,无需请求对象

类:Drupal\user\Access\PermissionAccessCheck

检查标识的值代表用户权限,账户对象满足即可进入,反之拒绝,多个权限使用分隔符分割,分隔符“+”表示OR关系,“,”表示AND关系,这两种分隔符不可一起使用,复杂判断可以使用自定义回调检查器来做。

此检查器涉及业务逻辑权限检查,将在业务逻辑检查中详述。

 

用户注册检查器access_check.user.register

检查标识: _access_user_register,方法:access,无需请求对象

类:Drupal\user\Access\RegisterAccessCheck

检查标识值随意,在系统开启用户注册后,用于注册的路由,只有未登陆用户才可以被允许

 

用户角色检查器access_check.user.role

检查标识: _role,方法:access,无需请求对象

类:Drupal\user\Access\RoleAccessCheck

检查标识的值代表用户角色,账户对象满足即可进入,反之拒绝,多个角色使用分隔符分割,分隔符“+”表示OR关系,“,”表示AND关系,这两种分隔符不可一起使用,复杂判断可以使用自定义回调检查器来做。

 

登陆状态检查器access_check.user.login_status

检查标识: _user_is_logged_in,方法:access,无需请求对象

类:Drupal\user\Access\LoginStatusCheck

在该检查标识的值中:true、"1", "true", "on" and "yes"表示已登陆状态,其他值表示未登陆状态,只有账户对象满足设定的状态才能访问,默认实现中账户对象的uid为0表示未登陆,否则指已经登陆。

 

 

 

以上就是入站检查的全部内容了,在下半部分中将介绍业务逻辑权限检查,也就是在管理后台中看到的账户、权限、角色以及涉及管理业务逻辑的权限检查等内容

 

补充注意:

1:为什么当前账户对象要使用一个代理服务?

系统中可能需要实例化多个账户对象,此方法可以识别来自链接的当前用户,同时它可以设置和取出带来很大灵活性

本书共98小节:

评论 (写第一个评论)