TokenService.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkAdmin
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2014~2021 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: https://gitee.com/zoujingli/ThinkLibrary
  8. // +----------------------------------------------------------------------
  9. // | 开源协议 ( https://mit-license.org )
  10. // +----------------------------------------------------------------------
  11. // | gitee 代码仓库:https://gitee.com/zoujingli/ThinkLibrary
  12. // | github 代码仓库:https://github.com/zoujingli/ThinkLibrary
  13. // +----------------------------------------------------------------------
  14. declare (strict_types=1);
  15. namespace think\admin\service;
  16. use think\admin\Service;
  17. /**
  18. * 表单令牌管理服务
  19. * Class TokenService
  20. * @package think\admin\service
  21. */
  22. class TokenService extends Service
  23. {
  24. /**
  25. * 缓存分组名称
  26. * @var string
  27. */
  28. private $name;
  29. /**
  30. * 当前缓存数据
  31. * @var array
  32. */
  33. private $cache = [];
  34. /**
  35. * 令牌有效时间
  36. * @var integer
  37. */
  38. private $expire = 600;
  39. /**
  40. * 令牌服务初始化
  41. */
  42. protected function initialize()
  43. {
  44. $this->name = $this->getCacheName();
  45. $this->cache = $this->_getCacheList(true);
  46. $this->app->event->listen('HttpEnd', function () {
  47. TokenService::instance()->saveCacheData();
  48. });
  49. }
  50. /**
  51. * 获取缓存名称
  52. * @return string
  53. */
  54. public function getCacheName(): string
  55. {
  56. $sid = $this->app->session->getId();
  57. return 'systoken_' . ($sid ?: 'default');
  58. }
  59. /**
  60. * 保存缓存到文件
  61. */
  62. public function saveCacheData()
  63. {
  64. $this->_clearTimeoutCache();
  65. $this->app->cache->set($this->name, $this->cache, $this->expire);
  66. }
  67. /**
  68. * 获取当前请求 CSRF 值
  69. * @return array|string
  70. */
  71. public function getInputToken(): string
  72. {
  73. return $this->app->request->header('user-form-token') ?: input('_csrf_', '');
  74. }
  75. /**
  76. * 验证 CSRF 是否有效
  77. * @param null|string $token 表单令牌
  78. * @param null|string $node 授权节点
  79. * @return boolean
  80. */
  81. public function checkFormToken(?string $token = null, ?string $node = null): bool
  82. {
  83. $cache = $this->_getCacheItem($token ?: $this->getInputToken());
  84. if (empty($cache['node']) || empty($cache['time'])) return false;
  85. return $cache['node'] === NodeService::instance()->fullnode($node);
  86. }
  87. /**
  88. * 清理表单 CSRF 数据
  89. * @param null|string $token
  90. * @return $this
  91. */
  92. public function clearFormToken(?string $token = null): TokenService
  93. {
  94. $this->_delCacheItem($token ?: $this->getInputToken());
  95. return $this;
  96. }
  97. /**
  98. * 生成表单 CSRF 数据
  99. * @param null|string $node
  100. * @return array
  101. */
  102. public function buildFormToken(?string $node = null): array
  103. {
  104. $cnode = NodeService::instance()->fullnode($node);
  105. [$token, $time] = [uniqid() . rand(100000, 999999), time()];
  106. $this->_setCacheItem($token, $item = ['node' => $cnode, 'time' => $time]);
  107. return array_merge($item, ['token' => $token]);
  108. }
  109. /**
  110. * 清空所有 CSRF 数据
  111. */
  112. public function clearCache()
  113. {
  114. $this->app->cache->delete($this->name);
  115. }
  116. /**
  117. * 设置缓存数据
  118. * @param string $token
  119. * @param array $value
  120. */
  121. private function _setCacheItem(string $token, array $value)
  122. {
  123. $this->cache[$token] = $value;
  124. }
  125. /**
  126. * 删除缓存
  127. * @param string $token
  128. */
  129. private function _delCacheItem(string $token)
  130. {
  131. unset($this->cache[$token]);
  132. }
  133. /**
  134. * 获取指定缓存
  135. * @param string $token
  136. * @return mixed
  137. */
  138. private function _getCacheItem(string $token)
  139. {
  140. $this->_clearTimeoutCache();
  141. return $this->cache[$token] ?? [];
  142. }
  143. /**
  144. * 获取缓存列表
  145. * @param bool $clear 强制清理
  146. * @return array
  147. */
  148. private function _getCacheList(bool $clear = false): array
  149. {
  150. $this->cache = $this->app->cache->get($this->name, []);
  151. if ($clear) $this->cache = $this->_clearTimeoutCache();
  152. return $this->cache;
  153. }
  154. /**
  155. * 清理超时的缓存
  156. * @return array
  157. */
  158. private function _clearTimeoutCache(): array
  159. {
  160. $time = time();
  161. foreach ($this->cache as $key => $item) {
  162. if (empty($item['time']) || $item['time'] + $this->expire < $time) {
  163. unset($this->cache[$key]);
  164. }
  165. }
  166. if (count($this->cache) > 999) {
  167. $this->cache = array_slice($this->cache, -999);
  168. }
  169. return $this->cache;
  170. }
  171. }