云客Drupal源码分析之权限系统(上)

本文详细介绍了Drupal权限系统的入站检查,包括权限系统的设计架构、入站检查的流程、用户账户对象的生成以及权限检查结果表示。通过分析,揭示了Drupal如何基于路由和会话管理来判断用户访问权限,涉及用户ID、会话ID、访问控制结果(AccessResult)的合并规则。此外,文章还讨论了权限检查器(AccessCheck)和检查提供器(CheckProvider)的角色和工作原理,以及如何自定义检查器进行权限控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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.youkuaiyun.com/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\AccessRe

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值