From 60ea1688b78aa127a14b119a8858fc1a08844337 Mon Sep 17 00:00:00 2001 From: gzydong <837215079@qq.com> Date: Sun, 23 May 2021 16:52:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Amqp/Producer/ChatMessageProducer.php | 2 +- app/Controller/Api/V1/AuthController.php | 38 +++---- app/Controller/Api/V1/CController.php | 4 +- app/Controller/WebSocketController.php | 24 ++--- app/Middleware/CorsMiddleware.php | 1 - app/Middleware/JWTAuthMiddleware.php | 48 +-------- app/Middleware/WebSocketAuthMiddleware.php | 13 +-- app/Model/User.php | 11 +- app/Service/UserService.php | 4 +- composer.json | 4 +- config/autoload/auth.php | 111 ++++++++++++++++++++ config/autoload/jwt.php | 115 --------------------- config/autoload/server.php | 2 +- 13 files changed, 156 insertions(+), 221 deletions(-) create mode 100644 config/autoload/auth.php delete mode 100644 config/autoload/jwt.php diff --git a/app/Amqp/Producer/ChatMessageProducer.php b/app/Amqp/Producer/ChatMessageProducer.php index ed7cdc6..34a3f0e 100644 --- a/app/Amqp/Producer/ChatMessageProducer.php +++ b/app/Amqp/Producer/ChatMessageProducer.php @@ -46,7 +46,7 @@ class ChatMessageProducer extends ProducerMessage public function __construct(string $event, array $data, array $options = []) { $message = [ - 'uuid' => uniqid(), + 'uuid' => uniqid((strval(mt_rand(0, 1000)))), 'event' => $event, 'data' => $data, 'options' => $options diff --git a/app/Controller/Api/V1/AuthController.php b/app/Controller/Api/V1/AuthController.php index fc5fb04..721f49c 100644 --- a/app/Controller/Api/V1/AuthController.php +++ b/app/Controller/Api/V1/AuthController.php @@ -13,12 +13,9 @@ namespace App\Controller\Api\V1; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\RequestMapping; -use Hyperf\HttpServer\Annotation\Middleware; -use App\Middleware\JWTAuthMiddleware; use App\Model\User; use App\Service\UserService; use App\Service\SmsCodeService; -use Phper666\JWTAuth\JWT; /** * 授权相关控制器 @@ -38,12 +35,6 @@ class AuthController extends CController */ private $smsCodeService; - /** - * @Inject - * @var JWT - */ - protected $jwt; - /** * 授权登录接口 * @RequestMapping(path="login", methods="post") @@ -63,10 +54,7 @@ class AuthController extends CController } try { - $token = $this->jwt->getToken([ - 'user_id' => $userInfo['id'], - 'platform' => $params['platform'], - ]); + $token = auth('jwt')->login($userInfo); } catch (\Exception $exception) { return $this->response->error('登录异常,请稍后再试!'); } @@ -74,14 +62,14 @@ class AuthController extends CController return $this->response->success([ 'authorize' => [ 'access_token' => $token, - 'expires_in' => $this->jwt->getTTL() + 'expires_in' => auth('jwt')->getJwtManager()->getTtl() ], 'user_info' => [ - 'nickname' => $userInfo['nickname'], - 'avatar' => $userInfo['avatar'], - 'gender' => $userInfo['gender'], - 'motto' => $userInfo['motto'], - 'email' => $userInfo['email'], + 'nickname' => $userInfo->nickname, + 'avatar' => $userInfo->avatar, + 'gender' => $userInfo->gender, + 'motto' => $userInfo->motto, + 'email' => $userInfo->email, ] ]); } @@ -89,11 +77,10 @@ class AuthController extends CController /** * 退出登录接口 * @RequestMapping(path="logout", methods="post") - * @Middleware(JWTAuthMiddleware::class) */ public function logout() { - $this->jwt->logout(); + auth('jwt')->check() && auth('jwt')->logout(); return $this->response->success([], 'Successfully logged out'); } @@ -164,14 +151,17 @@ class AuthController extends CController /** * 授权刷新接口 * @RequestMapping(path="refresh", methods="post") - * @Middleware(JWTAuthMiddleware::class) */ public function refresh() { + if (auth('jwt')->guest()) { + return $this->response->fail('登录 token 刷新失败!'); + } + return $this->response->success([ 'authorize' => [ - 'token' => $this->jwt->refreshToken(), - 'expire' => $this->jwt->getTTL() + 'token' => auth('jwt')->refresh(), + 'expire' => auth('jwt')->getJwtManager()->getTtl() ] ]); } diff --git a/app/Controller/Api/V1/CController.php b/app/Controller/Api/V1/CController.php index 44af527..7dc4510 100644 --- a/app/Controller/Api/V1/CController.php +++ b/app/Controller/Api/V1/CController.php @@ -34,6 +34,8 @@ class CController extends AbstractController */ public function uid() { - return $this->request->getAttribute('auth_data')['user_id'] ?? 0; + $guard = auth('jwt'); + + return $guard->check() ? $guard->user()->getId() : 0; } } diff --git a/app/Controller/WebSocketController.php b/app/Controller/WebSocketController.php index c6fb756..6ea7671 100644 --- a/app/Controller/WebSocketController.php +++ b/app/Controller/WebSocketController.php @@ -21,7 +21,6 @@ use Swoole\Http\Request; use Swoole\Websocket\Frame; use Swoole\Http\Response; use Swoole\WebSocket\Server; -use Phper666\JWTAuth\JWT; use App\Service\SocketClientService; use App\Service\MessageHandleService; use App\Model\Group\GroupMember; @@ -34,12 +33,6 @@ use App\Amqp\Producer\ChatMessageProducer; */ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface { - /** - * @Inject - * @var JWT - */ - private $jwt; - /** * @inject * @var SocketClientService @@ -68,14 +61,13 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos */ public function onOpen($server, Request $request): void { - $token = $request->get['token'] ?? ''; - $userInfo = $this->jwt->getParserData($token); - $userInfo['user_id'] = intval($userInfo['user_id']); + // 当前连接的用户 + $user_id = auth('jwt')->user()->getId(); - stdout_log()->notice("用户连接信息 : user_id:{$userInfo['user_id']} | fd:{$request->fd} 时间:" . date('Y-m-d H:i:s')); + stdout_log()->notice("用户连接信息 : user_id:{$user_id} | fd:{$request->fd} 时间:" . date('Y-m-d H:i:s')); // 判断是否存在异地登录 - $isOnline = $this->socketClientService->isOnlineAll($userInfo['user_id']); + $isOnline = $this->socketClientService->isOnlineAll($user_id); // 若开启单点登录,则主动关闭之前登录的连接 if ($isOnline) { @@ -83,18 +75,18 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos } // 绑定fd与用户关系 - $this->socketClientService->bindRelation($request->fd, $userInfo['user_id']); + $this->socketClientService->bindRelation($request->fd, $user_id); // 加入群聊 - $groupIds = GroupMember::getUserGroupIds($userInfo['user_id']); + $groupIds = GroupMember::getUserGroupIds($user_id); foreach ($groupIds as $group_id) { - SocketRoom::getInstance()->addRoomMember(strval($group_id), strval($userInfo['user_id'])); + SocketRoom::getInstance()->addRoomMember(strval($group_id), strval($user_id)); } if (!$isOnline) { // 推送消息至队列 push_amqp(new ChatMessageProducer(SocketConstants::EVENT_ONLINE_STATUS, [ - 'user_id' => $userInfo['user_id'], + 'user_id' => $user_id, 'status' => 1, 'notify' => '好友上线通知...' ])); diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index bcab019..3aa707b 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -24,7 +24,6 @@ class CorsMiddleware implements MiddlewareInterface $response = Context::get(ResponseInterface::class); $response = $response->withHeader('Access-Control-Allow-Origin', '*') ->withHeader('Access-Control-Allow-Credentials', 'true') - // Headers 可以根据实际情况进行改写。 ->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization'); Context::set(ResponseInterface::class, $response); diff --git a/app/Middleware/JWTAuthMiddleware.php b/app/Middleware/JWTAuthMiddleware.php index 274e050..a405de3 100644 --- a/app/Middleware/JWTAuthMiddleware.php +++ b/app/Middleware/JWTAuthMiddleware.php @@ -16,9 +16,6 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Phper666\JWTAuth\JWT; -use Phper666\JWTAuth\Util\JWTUtil; -use Hyperf\Utils\Context; /** * Http Token 授权验证中间件 @@ -37,60 +34,21 @@ class JWTAuthMiddleware implements MiddlewareInterface */ protected $response; - /** - * @var JWT - */ - protected $jwt; - - public function __construct(HttpResponse $response, RequestInterface $request, JWT $jwt) + public function __construct(HttpResponse $response, RequestInterface $request) { $this->response = $response; $this->request = $request; - $this->jwt = $jwt; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - $isValidToken = false; - - // 获取请求token - $token = $request->getHeaderLine('Authorization'); - if (empty($token)) { - $token = $this->request->input('token', ''); - } else { - $token = JWTUtil::handleToken($token); - } - - if (!empty($token)) { - try { - if ($token !== false && $this->jwt->checkToken($token)) { - $isValidToken = true; - } - } catch (\Exception $e) { - } - } - - if (!$isValidToken) { + if (auth('jwt')->guest()) { return $this->response->withStatus(401)->json([ 'code' => 401, - 'message' => 'Token authentication does not pass', + 'message' => 'Token authentication does not pass !', ]); } - $request = $this->setRequestContext($token); return $handler->handle($request); } - - private function setRequestContext(string $token): ServerRequestInterface - { - $request = Context::get(ServerRequestInterface::class); - - $jwtData = $this->jwt->getParserData($token); - - $request = $request->withAttribute('auth_data', $jwtData); - - Context::set(ServerRequestInterface::class, $request); - - return $request; - } } diff --git a/app/Middleware/WebSocketAuthMiddleware.php b/app/Middleware/WebSocketAuthMiddleware.php index 80a59cc..f119ead 100644 --- a/app/Middleware/WebSocketAuthMiddleware.php +++ b/app/Middleware/WebSocketAuthMiddleware.php @@ -11,7 +11,6 @@ declare(strict_types=1); namespace App\Middleware; -use Phper666\JWTAuth\JWT; use Hyperf\Di\Annotation\Inject; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; @@ -31,12 +30,6 @@ class WebSocketAuthMiddleware implements MiddlewareInterface */ protected $container; - /** - * @inject - * @var JWT - */ - private $jwt; - public function __construct(ContainerInterface $container) { $this->container = $container; @@ -45,11 +38,7 @@ class WebSocketAuthMiddleware implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // 授权验证拦截握手请求并实现权限检查 - $token = $request->getQueryParams()['token'] ?? ''; - - try { - $this->jwt->checkToken($token); - } catch (\Exception $e) { + if (auth('jwt')->guest()) { return $this->container->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->raw('Forbidden'); } diff --git a/app/Model/User.php b/app/Model/User.php index 4f62b00..8984eb2 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -4,6 +4,9 @@ declare (strict_types=1); namespace App\Model; +use Qbhy\HyperfAuth\AuthAbility; +use Qbhy\HyperfAuth\Authenticatable; + /** * Class User * @@ -18,8 +21,10 @@ namespace App\Model; * @property integer $created_at 注册时间 * @package App\Model */ -class User extends BaseModel +class User extends BaseModel implements Authenticatable { + use AuthAbility; + protected $table = 'users'; protected $fillable = [ @@ -34,4 +39,8 @@ class User extends BaseModel ]; protected $casts = []; + + protected $hidden = [ + 'password' + ]; } diff --git a/app/Service/UserService.php b/app/Service/UserService.php index d6ef44e..4907ef6 100644 --- a/app/Service/UserService.php +++ b/app/Service/UserService.php @@ -28,7 +28,7 @@ class UserService extends BaseService * * @param string $mobile 手机号 * @param string $password 登录密码 - * @return array|bool + * @return User|bool */ public function login(string $mobile, string $password) { @@ -40,7 +40,7 @@ class UserService extends BaseService return false; } - return $user->toArray(); + return $user; } /** diff --git a/composer.json b/composer.json index eabff06..669ac01 100644 --- a/composer.json +++ b/composer.json @@ -31,13 +31,13 @@ "hyperf/websocket-server": "^2.0", "hyperf/constants": "^2.0", "hyperf/validation": "^2.0", - "phper666/jwt-auth": "^3.0", "hyperf/filesystem": "^2.0", "hashids/hashids": "^4.0", "ext-json": "*", "hyperf/view": "^2.0", "hyperf/view-engine": "^2.0", - "phpmailer/phpmailer": "^6.2" + "phpmailer/phpmailer": "^6.2", + "96qbhy/hyperf-auth": "^2.3" }, "require-dev": { "swoole/ide-helper": "^4.5", diff --git a/config/autoload/auth.php b/config/autoload/auth.php new file mode 100644 index 0000000..6ae921d --- /dev/null +++ b/config/autoload/auth.php @@ -0,0 +1,111 @@ + [ + 'guard' => 'jwt', + 'provider' => 'users', + ], + 'guards' => [ + 'jwt' => [ + 'driver' => Qbhy\HyperfAuth\Guard\JwtGuard::class, + 'provider' => 'users', + + /* + * 以下是 simple-jwt 配置 + * 必填 + * jwt 服务端身份标识 + */ + 'secret' => env('SIMPLE_JWT_SECRET'), + + /* + * 可选配置 + * jwt 默认头部token使用的字段 + */ + 'header_name' => env('JWT_HEADER_NAME', 'Authorization'), + + /* + * 可选配置 + * jwt 生命周期,单位分钟 + */ + 'ttl' => (int)env('SIMPLE_JWT_TTL', 60 * 60), + + /* + * 可选配置 + * 允许过期多久以内的 token 进行刷新 + */ + 'refresh_ttl' => (int)env('SIMPLE_JWT_REFRESH_TTL', 60 * 60 * 24 * 7), + + /* + * 可选配置 + * 默认使用的加密类 + */ + 'default' => Encrypter\PasswordHashEncrypter::class, + + /* + * 可选配置 + * 加密类必须实现 Qbhy\SimpleJwt\Interfaces\Encrypter 接口 + */ + 'drivers' => [ + Encrypter\PasswordHashEncrypter::alg() => Encrypter\PasswordHashEncrypter::class, + Encrypter\CryptEncrypter::alg() => Encrypter\CryptEncrypter::class, + Encrypter\SHA1Encrypter::alg() => Encrypter\SHA1Encrypter::class, + Encrypter\Md5Encrypter::alg() => Encrypter\Md5Encrypter::class, + ], + + /* + * 可选配置 + * 编码类 + */ + 'encoder' => new Encoders\Base64UrlSafeEncoder(), + // 'encoder' => new Encoders\Base64Encoder(), + + /* + * 可选配置 + * 缓存类 + */ + //'cache' => new \Doctrine\Common\Cache\FilesystemCache(sys_get_temp_dir()), + // 如果需要分布式部署,请选择 redis 或者其他支持分布式的缓存驱动 + 'cache' => function () { + return make(\Qbhy\HyperfAuth\HyperfRedisCache::class); + }, + + /* + * 可选配置 + * 缓存前缀 + */ + 'prefix' => env('SIMPLE_JWT_PREFIX', 'default'), + ], + 'session' => [ + 'driver' => Qbhy\HyperfAuth\Guard\SessionGuard::class, + 'provider' => 'users', + ], + ], + 'providers' => [ + 'users' => [ + 'driver' => \Qbhy\HyperfAuth\Provider\EloquentProvider::class, + 'model' => App\Model\User::class, // 需要实现 Qbhy\HyperfAuth\Authenticatable 接口 + ], + ], +]; diff --git a/config/autoload/jwt.php b/config/autoload/jwt.php deleted file mode 100644 index 24a62fb..0000000 --- a/config/autoload/jwt.php +++ /dev/null @@ -1,115 +0,0 @@ - env('JWT_LOGIN_TYPE', 'mpop'), // 登录方式,sso为单点登录,mpop为多点登录 - - /** - * 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可 - */ - 'sso_key' => 'uid', - - 'secret' => env('JWT_SECRET', 'phper666'), // 非对称加密使用字符串,请使用自己加密的字符串 - - /** - * JWT 权限keys - * 对称算法: HS256, HS384 & HS512 使用 `JWT_SECRET`. - * 非对称算法: RS256, RS384 & RS512 / ES256, ES384 & ES512 使用下面的公钥私钥. - */ - 'keys' => [ - 'public' => env('JWT_PUBLIC_KEY'), // 公钥,例如:'file:///path/to/public/key' - 'private' => env('JWT_PRIVATE_KEY'), // 私钥,例如:'file:///path/to/private/key' - ], - - 'ttl' => env('JWT_TTL', 7200), // token过期时间,单位为秒 - - 'alg' => env('JWT_ALG', 'HS256'), // jwt的hearder加密算法 - - /** - * 支持的算法 - */ - 'supported_algs' => [ - 'HS256' => 'Lcobucci\JWT\Signer\Hmac\Sha256', - 'HS384' => 'Lcobucci\JWT\Signer\Hmac\Sha384', - 'HS512' => 'Lcobucci\JWT\Signer\Hmac\Sha512', - 'ES256' => 'Lcobucci\JWT\Signer\Ecdsa\Sha256', - 'ES384' => 'Lcobucci\JWT\Signer\Ecdsa\Sha384', - 'ES512' => 'Lcobucci\JWT\Signer\Ecdsa\Sha512', - 'RS256' => 'Lcobucci\JWT\Signer\Rsa\Sha256', - 'RS384' => 'Lcobucci\JWT\Signer\Rsa\Sha384', - 'RS512' => 'Lcobucci\JWT\Signer\Rsa\Sha512', - ], - - /** - * 对称算法名称 - */ - 'symmetry_algs' => [ - 'HS256', - 'HS384', - 'HS512' - ], - - /** - * 非对称算法名称 - */ - 'asymmetric_algs' => [ - 'RS256', - 'RS384', - 'RS512', - 'ES256', - 'ES384', - 'ES512', - ], - - /** - * 是否开启黑名单,单点登录和多点登录的注销、刷新使原token失效,必须要开启黑名单,目前黑名单缓存只支持hyperf缓存驱动 - */ - 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), - - /** - * 黑名单的宽限时间 单位为:秒,注意:如果使用单点登录,该宽限时间无效 - */ - 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), - - /** - * 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样 - */ - 'blacklist_cache_ttl' => env('JWT_TTL', 86400), - - 'blacklist_prefix' => 'phper666_jwt', // 黑名单缓存的前缀 - - /** - * 区分不同场景的token,比如你一个项目可能会有多种类型的应用接口鉴权,下面自行定义,我只是举例子 - * 下面的配置会自动覆盖根配置,比如application1会里面的数据会覆盖掉根数据 - * 下面的scene会和根数据合并 - * scene必须存在一个default - * 什么叫根数据,这个配置的一维数组,除了scene都叫根配置 - */ - 'scene' => [ - 'default' => [], - 'application1' => [ - 'secret' => 'application1', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 - 'sso_key' => 'uid', - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ], - 'application2' => [ - 'secret' => 'application2', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录 - 'sso_key' => 'uid', - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ], - 'application3' => [ - 'secret' => 'application3', // 非对称加密使用字符串,请使用自己加密的字符串 - 'login_type' => 'mppo', // 登录方式,sso为单点登录,mpop为多点登录 - 'ttl' => 7200, // token过期时间,单位为秒 - 'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样 - ] - ], - 'model' => [ // TODO 支持直接获取某模型的数据 - 'class' => '', - 'pk' => 'uid' - ] -]; diff --git a/config/autoload/server.php b/config/autoload/server.php index 304b401..f737de2 100644 --- a/config/autoload/server.php +++ b/config/autoload/server.php @@ -48,7 +48,7 @@ return [ ], 'settings' => [ 'enable_coroutine' => true, - 'worker_num' => swoole_cpu_num(), + 'worker_num' => 1, 'pid_file' => BASE_PATH . '/runtime/hyperf.pid', 'open_tcp_nodelay' => true, 'max_coroutine' => 100000,