TokenService.php 4.9 KB

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