From 26deccc36b4d6b48783a31540246be2c49ca991c Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sat, 2 Jan 2021 13:31:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=99=90=E5=88=B6=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E5=B8=90=E5=8F=B7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Tasks/CleanSessionTask.php | 43 ----- app/Console/Tasks/UpgradeTask.php | 12 +- app/Models/Online.php | 7 - app/Models/UserSession.php | 93 +++++++++++ app/Models/UserToken.php | 93 +++++++++++ app/Providers/Annotation.php | 7 +- app/Providers/MetaData.php | 7 +- app/Providers/Session.php | 4 +- app/Repos/UserSession.php | 24 +++ app/Repos/UserToken.php | 24 +++ app/Services/Auth/Api.php | 110 ++++++++---- app/Services/Auth/Home.php | 48 ++++++ app/Traits/Client.php | 8 + config/config.default.php | 52 +----- .../20210102041941_schema_202101021220.php | 158 ++++++++++++++++++ 15 files changed, 543 insertions(+), 147 deletions(-) delete mode 100644 app/Console/Tasks/CleanSessionTask.php create mode 100644 app/Models/UserSession.php create mode 100644 app/Models/UserToken.php create mode 100644 app/Repos/UserSession.php create mode 100644 app/Repos/UserToken.php create mode 100644 db/migrations/20210102041941_schema_202101021220.php diff --git a/app/Console/Tasks/CleanSessionTask.php b/app/Console/Tasks/CleanSessionTask.php deleted file mode 100644 index 7adbff5a..00000000 --- a/app/Console/Tasks/CleanSessionTask.php +++ /dev/null @@ -1,43 +0,0 @@ -getConfig(); - $redis = $this->getRedis(); - - $redis->select($config->path('session.db')); - - $keys = $this->querySessionKeys(10000); - - if (count($keys) == 0) return; - - $lifetime = $config->path('session.lifetime'); - - foreach ($keys as $key) { - $ttl = $redis->ttl($key); - $content = $redis->get($key); - if (empty($content) && $ttl < $lifetime * 0.5) { - $redis->del($key); - } - } - } - - /** - * 查找待清理会话 - * - * @param int $limit - * @return array - */ - protected function querySessionKeys($limit) - { - $cache = $this->getCache(); - - return $cache->queryKeys('_PHCR', $limit); - } - -} diff --git a/app/Console/Tasks/UpgradeTask.php b/app/Console/Tasks/UpgradeTask.php index 8700703d..0dbfa0dc 100644 --- a/app/Console/Tasks/UpgradeTask.php +++ b/app/Console/Tasks/UpgradeTask.php @@ -42,13 +42,9 @@ class UpgradeTask extends Task */ public function resetAnnotationAction() { - $config = $this->getConfig(); $redis = $this->getRedis(); - $dbIndex = $config->path('annotation.db'); - $statsKey = $config->path('annotation.statsKey'); - - $redis->select($dbIndex); + $statsKey = '_ANNOTATION_'; $keys = $redis->sMembers($statsKey); @@ -70,13 +66,9 @@ class UpgradeTask extends Task */ public function resetMetadataAction() { - $config = $this->getConfig(); $redis = $this->getRedis(); - $dbIndex = $config->path('metadata.db'); - $statsKey = $config->path('metadata.statsKey'); - - $redis->select($dbIndex); + $statsKey = '_METADATA_'; $keys = $redis->sMembers($statsKey); diff --git a/app/Models/Online.php b/app/Models/Online.php index 8d181b09..e00c62a2 100644 --- a/app/Models/Online.php +++ b/app/Models/Online.php @@ -19,13 +19,6 @@ class Online extends Model */ public $user_id; - /** - * 计划编号 - * - * @var string - */ - public $date; - /** * 客户端类型 * diff --git a/app/Models/UserSession.php b/app/Models/UserSession.php new file mode 100644 index 00000000..1e50498e --- /dev/null +++ b/app/Models/UserSession.php @@ -0,0 +1,93 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->create_time = time(); + } + + public function beforeUpdate() + { + $this->update_time = time(); + } + +} diff --git a/app/Models/UserToken.php b/app/Models/UserToken.php new file mode 100644 index 00000000..8a1ee4d3 --- /dev/null +++ b/app/Models/UserToken.php @@ -0,0 +1,93 @@ +addBehavior( + new SoftDelete([ + 'field' => 'deleted', + 'value' => 1, + ]) + ); + } + + public function beforeCreate() + { + $this->create_time = time(); + } + + public function beforeUpdate() + { + $this->update_time = time(); + } + +} diff --git a/app/Providers/Annotation.php b/app/Providers/Annotation.php index 3e524fcf..43e1e6b5 100644 --- a/app/Providers/Annotation.php +++ b/app/Providers/Annotation.php @@ -23,13 +23,14 @@ class Annotation extends Provider if ($config->get('env') == ENV_DEV) { $annotations = new MemoryAnnotations(); } else { + $statsKey = '_ANNOTATION_'; $annotations = new RedisAnnotations([ 'host' => $config->path('redis.host'), 'port' => $config->path('redis.port'), 'auth' => $config->path('redis.auth'), - 'index' => $config->path('annotation.db'), - 'lifetime' => $config->path('annotation.lifetime'), - 'statsKey' => $config->path('annotation.statsKey'), + 'lifetime' => $config->path('annotation.lifetime') ?: 30 * 86400, + 'prefix' => $statsKey . ':', + 'statsKey' => $statsKey, ]); } diff --git a/app/Providers/MetaData.php b/app/Providers/MetaData.php index 25a62363..cea48ce0 100644 --- a/app/Providers/MetaData.php +++ b/app/Providers/MetaData.php @@ -23,13 +23,14 @@ class MetaData extends Provider if ($config->get('env') == ENV_DEV) { $metaData = new MemoryMetaData(); } else { + $statsKey = '_METADATA_'; $metaData = new RedisMetaData([ 'host' => $config->path('redis.host'), 'port' => $config->path('redis.port'), 'auth' => $config->path('redis.auth'), - 'index' => $config->path('metadata.db'), - 'statsKey' => $config->path('metadata.statsKey'), - 'lifetime' => $config->path('metadata.lifetime'), + 'lifetime' => $config->path('metadata.lifetime') ?: 30 * 86400, + 'prefix' => $statsKey . ':', + 'statsKey' => $statsKey, ]); } diff --git a/app/Providers/Session.php b/app/Providers/Session.php index 995717e0..f8a11902 100644 --- a/app/Providers/Session.php +++ b/app/Providers/Session.php @@ -23,8 +23,8 @@ class Session extends Provider 'host' => $config->path('redis.host'), 'port' => $config->path('redis.port'), 'auth' => $config->path('redis.auth'), - 'index' => $config->path('session.db'), - 'lifetime' => $config->path('session.lifetime'), + 'lifetime' => $config->path('session.lifetime') ?: 24 * 3600, + 'prefix' => '_SESSION_:', ]); $session->start(); diff --git a/app/Repos/UserSession.php b/app/Repos/UserSession.php new file mode 100644 index 00000000..be56e73c --- /dev/null +++ b/app/Repos/UserSession.php @@ -0,0 +1,24 @@ +where('user_id = :user_id:', ['user_id' => $userId]) + ->andWhere('deleted = 0') + ->execute(); + } + +} diff --git a/app/Repos/UserToken.php b/app/Repos/UserToken.php new file mode 100644 index 00000000..70da5427 --- /dev/null +++ b/app/Repos/UserToken.php @@ -0,0 +1,24 @@ +where('user_id = :user_id:', ['user_id' => $userId]) + ->andWhere('deleted = 0') + ->execute(); + } + +} diff --git a/app/Services/Auth/Api.php b/app/Services/Auth/Api.php index fbc76eae..e1cf4a2c 100644 --- a/app/Services/Auth/Api.php +++ b/app/Services/Auth/Api.php @@ -3,70 +3,114 @@ namespace App\Services\Auth; use App\Models\User as UserModel; +use App\Models\UserToken as UserTokenModel; +use App\Repos\UserToken as UserTokenRepo; use App\Services\Auth as AuthService; -use Lcobucci\JWT\Builder as JwtBuilder; -use Lcobucci\JWT\Parser as JwtParser; -use Lcobucci\JWT\Signer\Hmac\Sha256 as JwtSingerSha256; -use Lcobucci\JWT\Signer\Key as JwtSingerKey; -use Lcobucci\JWT\ValidationData as JwtValidationData; +use App\Traits\Client as ClientTrait; class Api extends AuthService { + use ClientTrait; + public function saveAuthInfo(UserModel $user) { - $builder = new JwtBuilder(); + $token = $this->generateToken($user->id); + + $this->logoutOtherClients($user->id); + + $this->createUserToken($user->id, $token); + + $cache = $this->getCache(); + + $key = $this->getTokenCacheKey($token); + + $authInfo = [ + 'id' => $user->id, + 'name' => $user->name, + ]; $config = $this->getConfig(); - $expireTime = time() + $config->path('jwt.lifetime'); + $lifetime = $config->path('token.lifetime') ?: 7 * 86400; - $builder->expiresAt($expireTime); - $builder->withClaim('user_id', $user->id); - $builder->withClaim('user_name', $user->name); + $cache->save($key, $authInfo, $lifetime); - $singer = new JwtSingerSha256(); - - $key = new JwtSingerKey($config->path('jwt.key')); - - $token = $builder->getToken($singer, $key); - - return $token->__toString(); + return $token; } public function clearAuthInfo() { + $token = $this->request->getHeader('X-Token'); + if (empty($token)) return null; + + $cache = $this->getCache(); + + $key = $this->getTokenCacheKey($token); + + $cache->delete($key); } public function getAuthInfo() { - $authToken = $this->request->getHeader('X-Token'); + $token = $this->request->getHeader('X-Token'); - if (!$authToken) return null; + if (empty($token)) return null; - $config = $this->getConfig(); + $cache = $this->getCache(); - $parser = new JWTParser(); + $key = $this->getTokenCacheKey($token); - $token = $parser->parse($authToken); + $authInfo = $cache->get($key); - $data = new JWTValidationData(time(), $config->path('jwt.leeway')); + return $authInfo ?: null; + } - if (!$token->validate($data)) { - return null; + protected function createUserToken($userId, $token) + { + $userToken = new UserTokenModel(); + + $userToken->user_id = $userId; + $userToken->token = $token; + $userToken->client_type = $this->getClientType(); + $userToken->client_ip = $this->getClientIp(); + + $userToken->create(); + } + + protected function logoutOtherClients($userId) + { + $repo = new UserTokenRepo(); + + $records = $repo->findByUserId($userId); + + $cache = $this->getCache(); + + $clientType = $this->getClientType(); + + if ($records->count() == 0) { + return; } - $singer = new JwtSingerSha256(); - - if (!$token->verify($singer, $config->path('jwt.key'))) { - return null; + foreach ($records as $record) { + if ($record->client_type == $clientType) { + $record->deleted = 1; + $record->update(); + $key = $this->getTokenCacheKey($record->token); + $cache->delete($key); + } } + } - return [ - 'id' => $token->getClaim('user_id'), - 'name' => $token->getClaim('user_name'), - ]; + protected function generateToken($userId) + { + return md5(uniqid() . time() . $userId); + } + + protected function getTokenCacheKey($token) + { + return "_PHCR_TOKEN_:{$token}"; } } diff --git a/app/Services/Auth/Home.php b/app/Services/Auth/Home.php index dd8452da..6cafc47d 100644 --- a/app/Services/Auth/Home.php +++ b/app/Services/Auth/Home.php @@ -3,13 +3,24 @@ namespace App\Services\Auth; use App\Models\User as UserModel; +use App\Models\UserSession as UserSessionModel; +use App\Repos\UserSession as UserSessionRepo; use App\Services\Auth as AuthService; +use App\Traits\Client as ClientTrait; class Home extends AuthService { + use ClientTrait; + public function saveAuthInfo(UserModel $user) { + $sessionId = $this->session->getId(); + + $this->logoutOtherClients($user->id); + + $this->createUserSession($user->id, $sessionId); + $authKey = $this->getAuthKey(); $authInfo = [ @@ -41,4 +52,41 @@ class Home extends AuthService return 'home_auth_info'; } + protected function createUserSession($userId, $sessionId) + { + $userSession = new UserSessionModel(); + + $userSession->user_id = $userId; + $userSession->session_id = $sessionId; + $userSession->client_type = $this->getClientType(); + $userSession->client_ip = $this->getClientIp(); + + $userSession->create(); + } + + protected function logoutOtherClients($userId) + { + $cache = $this->getCache(); + + $repo = new UserSessionRepo(); + + $records = $repo->findByUserId($userId); + + if ($records->count() == 0) { + return; + } + + foreach ($records as $record) { + $record->deleted = 1; + $record->update(); + $key = $this->getSessionCacheKey($record->session_id); + $cache->delete($key); + } + } + + protected function getSessionCacheKey($sessionId) + { + return "_PHCR_SESSION_:{$sessionId}"; + } + } diff --git a/app/Traits/Client.php b/app/Traits/Client.php index 678a3415..ee9bd93a 100644 --- a/app/Traits/Client.php +++ b/app/Traits/Client.php @@ -27,6 +27,14 @@ trait Client */ $request = Di::getDefault()->get('request'); + $platform = $request->getHeader('X-Platform'); + + $types = array_flip(ClientModel::types()); + + if (!empty($platform) && isset($types[$platform])) { + return $types[$platform]; + } + $userAgent = $request->getServer('HTTP_USER_AGENT'); $result = new BrowserParser($userAgent); diff --git a/config/config.default.php b/config/config.default.php index dcdf4097..403ef71e 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -83,70 +83,30 @@ $config['redis']['port'] = 6379; $config['redis']['auth'] = '1qaz2wsx3edc'; /** - * redis库编号 - */ -$config['cache']['db'] = 0; - -/** - * 有效期(秒) + * 缓存有效期(秒) */ $config['cache']['lifetime'] = 24 * 3600; /** - * redis库编号 - */ -$config['session']['db'] = 1; - -/** - * 有效期(秒) + * 会话有效期(秒) */ $config['session']['lifetime'] = 24 * 3600; /** - * redis库编号 + * 令牌有效期(秒) */ -$config['metadata']['db'] = 2; +$config['token']['lifetime'] = 7 * 86400; /** - * 有效期(秒) + * 元数据有效期(秒) */ $config['metadata']['lifetime'] = 7 * 86400; /** - * statsKey - */ -$config['metadata']['statsKey'] = '_METADATA_'; - -/** - * redis库编号 - */ -$config['annotation']['db'] = 2; - -/** - * 有效期(秒) + * 注解有效期(秒) */ $config['annotation']['lifetime'] = 7 * 86400; -/** - * statsKey - */ -$config['annotation']['statsKey'] = '_ANNOTATION_'; - -/** - * 密钥 - */ -$config['jwt']['key'] = 'fu6ckEc8pv8k5K7m'; - -/** - * 有效期(秒) - */ -$config['jwt']['lifetime'] = 7 * 86400; - -/** - * 回旋时间(秒) - */ -$config['jwt']['leeway'] = 30; - /** * 允许跨域 */ diff --git a/db/migrations/20210102041941_schema_202101021220.php b/db/migrations/20210102041941_schema_202101021220.php new file mode 100644 index 00000000..1483af11 --- /dev/null +++ b/db/migrations/20210102041941_schema_202101021220.php @@ -0,0 +1,158 @@ +table('kg_user_session', [ + 'id' => false, + 'primary_key' => ['id'], + 'engine' => 'InnoDB', + 'encoding' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + 'comment' => '', + 'row_format' => 'DYNAMIC', + ]) + ->addColumn('id', 'integer', [ + 'null' => false, + 'limit' => MysqlAdapter::INT_REGULAR, + 'identity' => 'enable', + 'comment' => '主键编号', + ]) + ->addColumn('user_id', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '用户编号', + 'after' => 'id', + ]) + ->addColumn('session_id', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '会话编号', + 'after' => 'user_id', + ]) + ->addColumn('client_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '终端类型', + 'after' => 'session_id', + ]) + ->addColumn('client_ip', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '终端IP', + 'after' => 'client_type', + ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '删除标识', + 'after' => 'client_ip', + ]) + ->addColumn('create_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '创建时间', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '更新时间', + 'after' => 'create_time', + ]) + ->addIndex(['user_id'], [ + 'name' => 'user_id', + 'unique' => false, + ]) + ->create(); + $this->table('kg_user_token', [ + 'id' => false, + 'primary_key' => ['id'], + 'engine' => 'InnoDB', + 'encoding' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + 'comment' => '', + 'row_format' => 'DYNAMIC', + ]) + ->addColumn('id', 'integer', [ + 'null' => false, + 'limit' => MysqlAdapter::INT_REGULAR, + 'identity' => 'enable', + 'comment' => '主键编号', + ]) + ->addColumn('user_id', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '用户编号', + 'after' => 'id', + ]) + ->addColumn('token', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '身份令牌', + 'after' => 'user_id', + ]) + ->addColumn('client_type', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '终端类型', + 'after' => 'token', + ]) + ->addColumn('client_ip', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 64, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '终端IP', + 'after' => 'client_type', + ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '删除标识', + 'after' => 'client_ip', + ]) + ->addColumn('create_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '创建时间', + 'after' => 'deleted', + ]) + ->addColumn('update_time', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '更新时间', + 'after' => 'create_time', + ]) + ->addIndex(['user_id'], [ + 'name' => 'user_id', + 'unique' => false, + ]) + ->create(); + } + +}