初始化项目:添加后端代码、ThinkPHP框架、前端资源

This commit is contained in:
amb
2026-09-03 12:42:39 +08:00
commit 482bece22e
3726 changed files with 416708 additions and 0 deletions
@@ -0,0 +1,92 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\common\library\token;
/**
* Token基础类
*/
abstract class Driver
{
protected $handler = null;
protected $options = [];
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @param string|null $platform 平台标识(header-platform),仅审计用
* @return bool
*/
abstract function set($token, $user_id, $expire = 0, $platform = null);
/**
* 获取Token内的信息
* @param string $token
* @return array
*/
abstract function get($token);
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
abstract function check($token, $user_id);
/**
* 删除Token
* @param string $token
* @return boolean
*/
abstract function delete($token);
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
abstract function clear($user_id);
/**
* 返回句柄对象,可执行其它高级方法
*
* @access public
* @return object
*/
public function handler()
{
return $this->handler;
}
/**
* 获取加密后的Token
* @param string $token Token标识
* @return string
*/
protected function getEncryptedToken($token)
{
$config = \think\Config::get('token');
return hash_hmac($config['hashalgo'], $token, $config['key']);
}
/**
* 获取过期剩余时长
* @param $expiretime
* @return float|int|mixed
*/
protected function getExpiredIn($expiretime)
{
return $expiretime ? max(0, $expiretime - time()) : 365 * 86400;
}
}
@@ -0,0 +1,181 @@
<?php
namespace app\common\library\token\driver;
use app\common\library\token\Driver;
/**
* Token操作类
*/
class Mysql extends Driver
{
/**
* 默认配置
* @var array
*/
protected $options = [
'table' => 'user_token',
'expire' => 2592000,
'connection' => [],
];
/**
* 构造函数
* @param array $options 参数
* @access public
*/
public function __construct($options = [])
{
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
if ($this->options['connection']) {
$this->handler = \think\Db::connect($this->options['connection'])->name($this->options['table']);
} else {
$this->handler = \think\Db::name($this->options['table']);
}
$time = time();
$tokentime = cache('tokentime');
if (!$tokentime || $tokentime < $time - 86400) {
cache('tokentime', $time);
$this->handler->where('expiretime', '<', $time)->where('expiretime', '>', 0)->delete();
}
}
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @param string|null $platform 平台标识(header-platform),仅审计用
* @return bool
*/
public function set($token, $user_id, $expire = null, $platform = null)
{
$expiretime = !is_null($expire) && $expire !== 0 ? time() + $expire : 0;
$token = $this->getEncryptedToken($token);
$data = [
'token' => $token,
'user_id' => $user_id,
'createtime' => time(),
'expiretime' => $expiretime,
];
// platform 字段为审计用,避免老数据库未添加字段时报错
if (!is_null($platform) && $platform !== '') {
$data['platform'] = (string)$platform;
}
$this->handler->insert($data);
return true;
}
/**
* 获取新的查询构造器(避免与 $this->handler 的链式 where 状态相互污染)
* @return \think\db\Query
*/
protected function getQuery()
{
if (!empty($this->options['connection'])) {
return \think\Db::connect($this->options['connection'])->name($this->options['table']);
}
return \think\Db::name($this->options['table']);
}
/**
* 统计用户当前有效终端数(未过期的 Token 数量)
* @param int $user_id 会员ID
* @return int
*/
public function getUserTerminalCount($user_id)
{
$time = time();
return (int)$this->getQuery()
->where('user_id', $user_id)
->where(function ($query) use ($time) {
$query->where('expiretime', 0)
->whereOr('expiretime', '>', $time);
})
->count();
}
/**
* 删除该用户最早创建的一条有效 Token(按 createtime 升序)
* @param int $user_id 会员ID
* @return bool
*/
public function deleteOldestTerminal($user_id)
{
$time = time();
$row = $this->getQuery()
->where('user_id', $user_id)
->where(function ($query) use ($time) {
$query->where('expiretime', 0)
->whereOr('expiretime', '>', $time);
})
->order('createtime', 'asc')
->find();
if (!$row) {
return false;
}
$this->getQuery()->where('token', $row['token'])->delete();
return true;
}
/**
* 获取Token内的信息
* @param string $token
* @return array
*/
public function get($token)
{
$data = $this->handler->where('token', $this->getEncryptedToken($token))->find();
if ($data) {
if (!$data['expiretime'] || $data['expiretime'] > time()) {
//返回未加密的token给客户端使用
$data['token'] = $token;
//返回剩余有效时间
$data['expires_in'] = $this->getExpiredIn($data['expiretime']);
return $data;
} else {
self::delete($token);
}
}
return [];
}
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
public function check($token, $user_id)
{
$data = $this->get($token);
return $data && $data['user_id'] == $user_id ? true : false;
}
/**
* 删除Token
* @param string $token
* @return boolean
*/
public function delete($token)
{
$this->handler->where('token', $this->getEncryptedToken($token))->delete();
return true;
}
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
public function clear($user_id)
{
$this->handler->where('user_id', $user_id)->delete();
return true;
}
}
@@ -0,0 +1,212 @@
<?php
namespace app\common\library\token\driver;
use app\common\library\token\Driver;
/**
* Token操作类
*/
class Redis extends Driver
{
protected $options = [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'expire' => 0,
'persistent' => false,
'userprefix' => 'up:',
'tokenprefix' => 'tp:',
];
/**
* 构造函数
* @param array $options 缓存参数
* @throws \BadFunctionCallException
* @access public
*/
public function __construct($options = [])
{
if (!extension_loaded('redis')) {
throw new \BadFunctionCallException('not support: redis');
}
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->handler = new \Redis;
if ($this->options['persistent']) {
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
} else {
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
}
if ('' != $this->options['password']) {
$this->handler->auth($this->options['password']);
}
if (0 != $this->options['select']) {
$this->handler->select($this->options['select']);
}
}
/**
* 获取加密后的Token
* @param string $token Token标识
* @return string
*/
protected function getEncryptedToken($token)
{
$config = \think\Config::get('token');
return $this->options['tokenprefix'] . hash_hmac($config['hashalgo'], $token, $config['key']);
}
/**
* 获取会员的key
* @param $user_id
* @return string
*/
protected function getUserKey($user_id)
{
return $this->options['userprefix'] . $user_id;
}
/**
* 存储Token
* @param string $token Token
* @param int $user_id 会员ID
* @param int $expire 过期时长,0表示无限,单位秒
* @param string|null $platform 平台标识(header-platform),仅审计用
* @return bool
*/
public function set($token, $user_id, $expire = 0, $platform = null)
{
if (is_null($expire)) {
$expire = $this->options['expire'];
}
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
$key = $this->getEncryptedToken($token);
if ($expire) {
$result = $this->handler->setex($key, $expire, $user_id);
} else {
$result = $this->handler->set($key, $user_id);
}
//写入会员关联的token
$this->handler->sAdd($this->getUserKey($user_id), $key);
return $result;
}
/**
* 统计用户当前有效终端数(未过期的 Token 数量)
* 注意:Redis Set 中的 key 可能已过期但未从 Set 中清理,这里会同步清理
* @param int $user_id 会员ID
* @return int
*/
public function getUserTerminalCount($user_id)
{
$userKey = $this->getUserKey($user_id);
$keys = $this->handler->sMembers($userKey) ?: [];
$count = 0;
foreach ($keys as $key) {
if ($this->handler->exists($key)) {
$count++;
} else {
// 清理已失效的 key
$this->handler->sRem($userKey, $key);
}
}
return $count;
}
/**
* 删除该用户最早创建的一条有效 Token
* Redis 没有 createtime 概念,按 Set 中第一条仍然存活的 key 删除
* @param int $user_id 会员ID
* @return bool
*/
public function deleteOldestTerminal($user_id)
{
$userKey = $this->getUserKey($user_id);
$keys = $this->handler->sMembers($userKey) ?: [];
foreach ($keys as $key) {
if ($this->handler->exists($key)) {
$this->handler->del($key);
$this->handler->sRem($userKey, $key);
return true;
}
// 顺手清理已过期的 key
$this->handler->sRem($userKey, $key);
}
return false;
}
/**
* 获取Token内的信息
* @param string $token
* @return array
*/
public function get($token)
{
$key = $this->getEncryptedToken($token);
$value = $this->handler->get($key);
if (is_null($value) || false === $value) {
return [];
}
//获取有效期
$expire = $this->handler->ttl($key);
$expire = $expire < 0 ? 365 * 86400 : $expire;
$expiretime = time() + $expire;
//解决使用redis方式储存token时api接口Token刷新与检测因expires_in拼写错误报错的BUG
$result = ['token' => $token, 'user_id' => $value, 'expiretime' => $expiretime, 'expires_in' => $expire];
return $result;
}
/**
* 判断Token是否可用
* @param string $token Token
* @param int $user_id 会员ID
* @return boolean
*/
public function check($token, $user_id)
{
$data = self::get($token);
return $data && $data['user_id'] == $user_id ? true : false;
}
/**
* 删除Token
* @param string $token
* @return boolean
*/
public function delete($token)
{
$data = $this->get($token);
if ($data) {
$key = $this->getEncryptedToken($token);
$user_id = $data['user_id'];
$this->handler->del($key);
$this->handler->sRem($this->getUserKey($user_id), $key);
}
return true;
}
/**
* 删除指定用户的所有Token
* @param int $user_id
* @return boolean
*/
public function clear($user_id)
{
$keys = $this->handler->sMembers($this->getUserKey($user_id));
$this->handler->del($this->getUserKey($user_id));
$this->handler->del($keys);
return true;
}
}