优化代码

main
gzydong 2021-05-23 16:52:01 +08:00
parent ddae0895a3
commit 60ea1688b7
13 changed files with 156 additions and 221 deletions

View File

@ -46,7 +46,7 @@ class ChatMessageProducer extends ProducerMessage
public function __construct(string $event, array $data, array $options = []) public function __construct(string $event, array $data, array $options = [])
{ {
$message = [ $message = [
'uuid' => uniqid(), 'uuid' => uniqid((strval(mt_rand(0, 1000)))),
'event' => $event, 'event' => $event,
'data' => $data, 'data' => $data,
'options' => $options 'options' => $options

View File

@ -13,12 +13,9 @@ namespace App\Controller\Api\V1;
use Hyperf\Di\Annotation\Inject; use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping; use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\HttpServer\Annotation\Middleware;
use App\Middleware\JWTAuthMiddleware;
use App\Model\User; use App\Model\User;
use App\Service\UserService; use App\Service\UserService;
use App\Service\SmsCodeService; use App\Service\SmsCodeService;
use Phper666\JWTAuth\JWT;
/** /**
* 授权相关控制器 * 授权相关控制器
@ -38,12 +35,6 @@ class AuthController extends CController
*/ */
private $smsCodeService; private $smsCodeService;
/**
* @Inject
* @var JWT
*/
protected $jwt;
/** /**
* 授权登录接口 * 授权登录接口
* @RequestMapping(path="login", methods="post") * @RequestMapping(path="login", methods="post")
@ -63,10 +54,7 @@ class AuthController extends CController
} }
try { try {
$token = $this->jwt->getToken([ $token = auth('jwt')->login($userInfo);
'user_id' => $userInfo['id'],
'platform' => $params['platform'],
]);
} catch (\Exception $exception) { } catch (\Exception $exception) {
return $this->response->error('登录异常,请稍后再试!'); return $this->response->error('登录异常,请稍后再试!');
} }
@ -74,14 +62,14 @@ class AuthController extends CController
return $this->response->success([ return $this->response->success([
'authorize' => [ 'authorize' => [
'access_token' => $token, 'access_token' => $token,
'expires_in' => $this->jwt->getTTL() 'expires_in' => auth('jwt')->getJwtManager()->getTtl()
], ],
'user_info' => [ 'user_info' => [
'nickname' => $userInfo['nickname'], 'nickname' => $userInfo->nickname,
'avatar' => $userInfo['avatar'], 'avatar' => $userInfo->avatar,
'gender' => $userInfo['gender'], 'gender' => $userInfo->gender,
'motto' => $userInfo['motto'], 'motto' => $userInfo->motto,
'email' => $userInfo['email'], 'email' => $userInfo->email,
] ]
]); ]);
} }
@ -89,11 +77,10 @@ class AuthController extends CController
/** /**
* 退出登录接口 * 退出登录接口
* @RequestMapping(path="logout", methods="post") * @RequestMapping(path="logout", methods="post")
* @Middleware(JWTAuthMiddleware::class)
*/ */
public function logout() public function logout()
{ {
$this->jwt->logout(); auth('jwt')->check() && auth('jwt')->logout();
return $this->response->success([], 'Successfully logged out'); return $this->response->success([], 'Successfully logged out');
} }
@ -164,14 +151,17 @@ class AuthController extends CController
/** /**
* 授权刷新接口 * 授权刷新接口
* @RequestMapping(path="refresh", methods="post") * @RequestMapping(path="refresh", methods="post")
* @Middleware(JWTAuthMiddleware::class)
*/ */
public function refresh() public function refresh()
{ {
if (auth('jwt')->guest()) {
return $this->response->fail('登录 token 刷新失败!');
}
return $this->response->success([ return $this->response->success([
'authorize' => [ 'authorize' => [
'token' => $this->jwt->refreshToken(), 'token' => auth('jwt')->refresh(),
'expire' => $this->jwt->getTTL() 'expire' => auth('jwt')->getJwtManager()->getTtl()
] ]
]); ]);
} }

View File

@ -34,6 +34,8 @@ class CController extends AbstractController
*/ */
public function uid() public function uid()
{ {
return $this->request->getAttribute('auth_data')['user_id'] ?? 0; $guard = auth('jwt');
return $guard->check() ? $guard->user()->getId() : 0;
} }
} }

View File

@ -21,7 +21,6 @@ use Swoole\Http\Request;
use Swoole\Websocket\Frame; use Swoole\Websocket\Frame;
use Swoole\Http\Response; use Swoole\Http\Response;
use Swoole\WebSocket\Server; use Swoole\WebSocket\Server;
use Phper666\JWTAuth\JWT;
use App\Service\SocketClientService; use App\Service\SocketClientService;
use App\Service\MessageHandleService; use App\Service\MessageHandleService;
use App\Model\Group\GroupMember; use App\Model\Group\GroupMember;
@ -34,12 +33,6 @@ use App\Amqp\Producer\ChatMessageProducer;
*/ */
class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{ {
/**
* @Inject
* @var JWT
*/
private $jwt;
/** /**
* @inject * @inject
* @var SocketClientService * @var SocketClientService
@ -68,14 +61,13 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
*/ */
public function onOpen($server, Request $request): void public function onOpen($server, Request $request): void
{ {
$token = $request->get['token'] ?? ''; // 当前连接的用户
$userInfo = $this->jwt->getParserData($token); $user_id = auth('jwt')->user()->getId();
$userInfo['user_id'] = intval($userInfo['user_id']);
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) { if ($isOnline) {
@ -83,18 +75,18 @@ class WebSocketController implements OnMessageInterface, OnOpenInterface, OnClos
} }
// 绑定fd与用户关系 // 绑定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) { 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) { if (!$isOnline) {
// 推送消息至队列 // 推送消息至队列
push_amqp(new ChatMessageProducer(SocketConstants::EVENT_ONLINE_STATUS, [ push_amqp(new ChatMessageProducer(SocketConstants::EVENT_ONLINE_STATUS, [
'user_id' => $userInfo['user_id'], 'user_id' => $user_id,
'status' => 1, 'status' => 1,
'notify' => '好友上线通知...' 'notify' => '好友上线通知...'
])); ]));

View File

@ -24,7 +24,6 @@ class CorsMiddleware implements MiddlewareInterface
$response = Context::get(ResponseInterface::class); $response = Context::get(ResponseInterface::class);
$response = $response->withHeader('Access-Control-Allow-Origin', '*') $response = $response->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Credentials', 'true') ->withHeader('Access-Control-Allow-Credentials', 'true')
// Headers 可以根据实际情况进行改写。
->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization'); ->withHeader('Access-Control-Allow-Headers', 'DNT,Keep-Alive,User-Agent,Cache-Control,Content-Type,Authorization');
Context::set(ResponseInterface::class, $response); Context::set(ResponseInterface::class, $response);

View File

@ -16,9 +16,6 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Phper666\JWTAuth\JWT;
use Phper666\JWTAuth\Util\JWTUtil;
use Hyperf\Utils\Context;
/** /**
* Http Token 授权验证中间件 * Http Token 授权验证中间件
@ -37,60 +34,21 @@ class JWTAuthMiddleware implements MiddlewareInterface
*/ */
protected $response; protected $response;
/** public function __construct(HttpResponse $response, RequestInterface $request)
* @var JWT
*/
protected $jwt;
public function __construct(HttpResponse $response, RequestInterface $request, JWT $jwt)
{ {
$this->response = $response; $this->response = $response;
$this->request = $request; $this->request = $request;
$this->jwt = $jwt;
} }
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{ {
$isValidToken = false; if (auth('jwt')->guest()) {
// 获取请求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) {
return $this->response->withStatus(401)->json([ return $this->response->withStatus(401)->json([
'code' => 401, 'code' => 401,
'message' => 'Token authentication does not pass', 'message' => 'Token authentication does not pass !',
]); ]);
} }
$request = $this->setRequestContext($token);
return $handler->handle($request); 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;
}
} }

View File

@ -11,7 +11,6 @@ declare(strict_types=1);
namespace App\Middleware; namespace App\Middleware;
use Phper666\JWTAuth\JWT;
use Hyperf\Di\Annotation\Inject; use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
@ -31,12 +30,6 @@ class WebSocketAuthMiddleware implements MiddlewareInterface
*/ */
protected $container; protected $container;
/**
* @inject
* @var JWT
*/
private $jwt;
public function __construct(ContainerInterface $container) public function __construct(ContainerInterface $container)
{ {
$this->container = $container; $this->container = $container;
@ -45,11 +38,7 @@ class WebSocketAuthMiddleware implements MiddlewareInterface
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{ {
// 授权验证拦截握手请求并实现权限检查 // 授权验证拦截握手请求并实现权限检查
$token = $request->getQueryParams()['token'] ?? ''; if (auth('jwt')->guest()) {
try {
$this->jwt->checkToken($token);
} catch (\Exception $e) {
return $this->container->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->raw('Forbidden'); return $this->container->get(\Hyperf\HttpServer\Contract\ResponseInterface::class)->raw('Forbidden');
} }

View File

@ -4,6 +4,9 @@ declare (strict_types=1);
namespace App\Model; namespace App\Model;
use Qbhy\HyperfAuth\AuthAbility;
use Qbhy\HyperfAuth\Authenticatable;
/** /**
* Class User * Class User
* *
@ -18,8 +21,10 @@ namespace App\Model;
* @property integer $created_at 注册时间 * @property integer $created_at 注册时间
* @package App\Model * @package App\Model
*/ */
class User extends BaseModel class User extends BaseModel implements Authenticatable
{ {
use AuthAbility;
protected $table = 'users'; protected $table = 'users';
protected $fillable = [ protected $fillable = [
@ -34,4 +39,8 @@ class User extends BaseModel
]; ];
protected $casts = []; protected $casts = [];
protected $hidden = [
'password'
];
} }

View File

@ -28,7 +28,7 @@ class UserService extends BaseService
* *
* @param string $mobile 手机号 * @param string $mobile 手机号
* @param string $password 登录密码 * @param string $password 登录密码
* @return array|bool * @return User|bool
*/ */
public function login(string $mobile, string $password) public function login(string $mobile, string $password)
{ {
@ -40,7 +40,7 @@ class UserService extends BaseService
return false; return false;
} }
return $user->toArray(); return $user;
} }
/** /**

View File

@ -31,13 +31,13 @@
"hyperf/websocket-server": "^2.0", "hyperf/websocket-server": "^2.0",
"hyperf/constants": "^2.0", "hyperf/constants": "^2.0",
"hyperf/validation": "^2.0", "hyperf/validation": "^2.0",
"phper666/jwt-auth": "^3.0",
"hyperf/filesystem": "^2.0", "hyperf/filesystem": "^2.0",
"hashids/hashids": "^4.0", "hashids/hashids": "^4.0",
"ext-json": "*", "ext-json": "*",
"hyperf/view": "^2.0", "hyperf/view": "^2.0",
"hyperf/view-engine": "^2.0", "hyperf/view-engine": "^2.0",
"phpmailer/phpmailer": "^6.2" "phpmailer/phpmailer": "^6.2",
"96qbhy/hyperf-auth": "^2.3"
}, },
"require-dev": { "require-dev": {
"swoole/ide-helper": "^4.5", "swoole/ide-helper": "^4.5",

111
config/autoload/auth.php Normal file
View File

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
/**
* This file is part of qbhy/hyperf-auth.
*
* @link https://github.com/qbhy/hyperf-auth
* @document https://github.com/qbhy/hyperf-auth/blob/master/README.md
* @contact qbhy0715@qq.com
* @license https://github.com/qbhy/hyperf-auth/blob/master/LICENSE
*/
use Qbhy\SimpleJwt\Encoders;
use Qbhy\SimpleJwt\EncryptAdapters as Encrypter;
/*
* This file is part of qbhy/hyperf-auth.
*
* @link https://github.com/qbhy/hyperf-auth
* @document https://github.com/qbhy/hyperf-auth/blob/master/README.md
* @contact qbhy0715@qq.com
* @license https://github.com/qbhy/hyperf-auth/blob/master/LICENSE
*/
return [
'default' => [
'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 接口
],
],
];

View File

@ -1,115 +0,0 @@
<?php
declare(strict_types=1);
return [
'login_type' => 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'
]
];

View File

@ -48,7 +48,7 @@ return [
], ],
'settings' => [ 'settings' => [
'enable_coroutine' => true, 'enable_coroutine' => true,
'worker_num' => swoole_cpu_num(), 'worker_num' => 1,
'pid_file' => BASE_PATH . '/runtime/hyperf.pid', 'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
'open_tcp_nodelay' => true, 'open_tcp_nodelay' => true,
'max_coroutine' => 100000, 'max_coroutine' => 100000,