mirror of
http://124.126.16.154:8888/singularity/hdk-auth.git
synced 2026-01-15 05:35:07 +08:00
init: hyperf-auth
This commit is contained in:
@@ -1,32 +1,40 @@
|
||||
{
|
||||
"name": "singularity/auth",
|
||||
"name": "singularity/hdk-auth",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"description": "登录控制",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Example\\": "src/Example/"
|
||||
"Singularity\\HDK\\Auth\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\Example\\": "tests/Example/"
|
||||
"extra": {
|
||||
"hyperf": {
|
||||
"config": "Singularity\\HDK\\Auth\\ConfigProvider"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"composer/composer": "~2.0.14"
|
||||
"php": ">=7.4",
|
||||
"composer/composer": "~2.0.14",
|
||||
"hyperf/validation": "^2.2.33",
|
||||
"singularity/hdk-core": "0.1.x-dev",
|
||||
"symfony/polyfill-php80": "^1.27",
|
||||
"ext-redis": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"firebase/php-jwt": "^6.3"
|
||||
},
|
||||
"suggest": {
|
||||
"firebase/php-jwt": "^6.3"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
"sort-packages": true,
|
||||
"secure-http": false
|
||||
},
|
||||
"extra": [],
|
||||
"scripts": {
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
@@ -36,6 +44,10 @@
|
||||
"analyse": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config"
|
||||
},
|
||||
"repositories": {
|
||||
"lux-map": {
|
||||
"type": "composer",
|
||||
"url": "https://satis.luxcreo.cn/"
|
||||
},
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.aliyun.com/composer"
|
||||
|
||||
6782
composer.lock
generated
6782
composer.lock
generated
File diff suppressed because it is too large
Load Diff
29
docs/overview.md
Normal file
29
docs/overview.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 概览
|
||||
|
||||
[TOC]
|
||||
|
||||
## 模块
|
||||
|
||||
### 认证
|
||||
* ***(规范)*** 对接口进行鉴权,推荐使用 *验证器 + Trait* 的形式。可选:
|
||||
* 不需要鉴权:
|
||||
* PublicRequest
|
||||
* 需要鉴权:
|
||||
* LoginRequiredRequest
|
||||
* *(灵活)* 对路由、控制器、接口进行认证鉴权,可以使用中间件的方式
|
||||
|
||||
### SSO
|
||||
|
||||
### 鉴权
|
||||
|
||||
### 用户信息交互(SDK)
|
||||
通过对 RPC 请求进行封装,包装成类普通数据库操作的 Service,对调用者隐藏 Guzzle/cURL 过程。
|
||||
|
||||
将用于交互的用户信息、收货地址等,包装成 Resource,类似 Model 的方式进行调用。
|
||||
|
||||
## 认证方式
|
||||
### Session/Cookie
|
||||
|
||||
### JWT
|
||||
|
||||
### Token
|
||||
@@ -1,4 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Singularity\HDK\Auth;
|
||||
|
||||
use Hyperf\Contract\StdoutLoggerInterface;
|
||||
use Hyperf\Framework\Logger\StdoutLogger;
|
||||
|
||||
/**
|
||||
* ConfigProvider.php@HyperfAuth
|
||||
*
|
||||
@@ -7,4 +15,56 @@
|
||||
* Created on 2023/1/16
|
||||
*/
|
||||
|
||||
return [];
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
/** @noinspection PhpUndefinedConstantInspection */
|
||||
return [
|
||||
// 合并到 config/autoload/dependencies.php 文件
|
||||
'dependencies' => [
|
||||
StdoutLoggerInterface::class => StdoutLogger::class,
|
||||
],
|
||||
// 合并到 config/autoload/annotations.php 文件
|
||||
'annotations' => [
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
__DIR__,
|
||||
],
|
||||
],
|
||||
],
|
||||
// 默认 Command 的定义,合并到 Hyperf\Contract\ConfigInterface 内,换个方式理解也就是与 config/autoload/commands.php 对应
|
||||
'commands' => [
|
||||
],
|
||||
// 与 commands 类似
|
||||
'listeners' => [],
|
||||
// 组件默认配置文件,即执行命令后会把 source 的对应的文件复制为 destination 对应的的文件
|
||||
'publish' => [
|
||||
[
|
||||
'id' => 'config',
|
||||
'description' => 'The common config for HDK',
|
||||
'source' => __DIR__ . '/../publish/common.php',
|
||||
'destination' => BASE_PATH . '/config/autoload/common.php',
|
||||
],
|
||||
[
|
||||
'id' => 'script',
|
||||
'description' => 'The common script for HDK',
|
||||
'source' => __DIR__ . '/../publish/scripts/docker-env.sh',
|
||||
'destination' => BASE_PATH . '/scripts/docker-env.sh',
|
||||
],
|
||||
[
|
||||
'id' => 'languages_cn',
|
||||
'description' => 'The common error message for Chinese',
|
||||
'source' => __DIR__ . '/../publish/languages/zh_CN/common_error.php',
|
||||
'destination' => BASE_PATH . '/storage/languages/zh_CN/common_error.php',
|
||||
],
|
||||
[
|
||||
'id' => 'languages_en',
|
||||
'description' => 'The common error message for English',
|
||||
'source' => __DIR__ . '/../publish/languages/en/common_error.php',
|
||||
'destination' => BASE_PATH . '/storage/languages/en/common_error.php',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
48
src/Middleware/AuthenticateMiddleware.php
Normal file
48
src/Middleware/AuthenticateMiddleware.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Middleware;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Singularity\HDK\Auth\Services\AuthenticationInterface;
|
||||
use Singularity\HDK\Core\Constants\CommonErrorCode;
|
||||
use Singularity\HDK\Core\Enumerations\Http\Header\RFCs\RFC7486;
|
||||
use Singularity\HDK\Core\Exceptions\Unauthorized;
|
||||
|
||||
/**
|
||||
* 通用鉴权中间件
|
||||
* Singularity\HDK\Auth\Middleware\AuthenticateMiddleware@HyperfAuth
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
class AuthenticateMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var AuthenticationInterface
|
||||
*/
|
||||
private AuthenticationInterface $authentication;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function process(
|
||||
ServerRequestInterface $request,
|
||||
RequestHandlerInterface $handler
|
||||
): ResponseInterface {
|
||||
$token = $this->authentication->parseTokenFromHeaders();
|
||||
if (empty($token) || $token === 'null' || $token === 'undefined' || $token === 'false') {
|
||||
throw new Unauthorized(CommonErrorCode::UNAUTHORIZED, null, RFC7486::HOBA);
|
||||
}
|
||||
|
||||
// 验证 token 中的参数
|
||||
$this->authentication->verified($token);
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
26
src/Request/LoginRequiredRequest.php
Normal file
26
src/Request/LoginRequiredRequest.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* LoginRequiredRequest.php@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Request;
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
use Singularity\HDK\Auth\Traits\LoginRequired;
|
||||
|
||||
/**
|
||||
* 需要登录认证的接口
|
||||
* Singularity\HDK\Account\Request\LoginRequiredRequest@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
abstract class LoginRequiredRequest extends FormRequest
|
||||
{
|
||||
use LoginRequired;
|
||||
}
|
||||
26
src/Request/PublicRequest.php
Normal file
26
src/Request/PublicRequest.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* AbstractRequest.php@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Request;
|
||||
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
use Singularity\HDK\Auth\Traits\LoginOptional;
|
||||
|
||||
/**
|
||||
* 不需要登录就可以使用的接口
|
||||
* Singularity\HDK\Account\Request\AbstractRequest@HyperfAuth
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
abstract class PublicRequest extends FormRequest
|
||||
{
|
||||
use LoginOptional;
|
||||
}
|
||||
32
src/Resource/JsonWebToken.php
Normal file
32
src/Resource/JsonWebToken.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Resource;
|
||||
|
||||
use Hyperf\Resource\Json\JsonResource;
|
||||
use Singularity\HDK\Core\Resource\ClassicResponse;
|
||||
|
||||
/**
|
||||
* JWT 数据模型
|
||||
*
|
||||
* @property string $iss JWT 签发者
|
||||
* @property string $sub 面向的用户
|
||||
* @property string $aud 接收 JWT 的一方
|
||||
* @property int $exp 过期时间
|
||||
* @property int $nbf 生效时间
|
||||
* @property int $iat 签发时间
|
||||
* @property string $uid 用户唯一标识
|
||||
*/
|
||||
class JsonWebToken extends JsonResource
|
||||
{
|
||||
use ClassicResponse;
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return parent::toArray();
|
||||
}
|
||||
}
|
||||
55
src/Resource/User.php
Normal file
55
src/Resource/User.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* User.php@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/28
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Resource;
|
||||
|
||||
use Hyperf\Resource\Json\JsonResource;
|
||||
use Singularity\HDK\Core\Resource\ClassicResponse;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
* Singularity\HyperfDevelopmentKit\Account\Resource\ User@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/28
|
||||
*
|
||||
* @property string $uid 对外唯一标识
|
||||
* @property string $username 用户名,用于登录(默认中国大陆为手机号,其它为邮箱)
|
||||
* @property string $secPhone 安全手机号,用于登录
|
||||
* @property string $secEmail 安全邮箱,用于登录
|
||||
* @property string $name 昵称、姓名
|
||||
* @property bool $hasPassword 是否设置了密码
|
||||
* @property string $avatar 用户头像地址
|
||||
* @property int $gender 性别,1:男,0:女
|
||||
* @property string $birthday 生日
|
||||
* @property string $contactPhone 联系电话
|
||||
* @property string $contactEmail 联系邮箱
|
||||
* @property string $company 公司名称
|
||||
* @property string $lastLoginTimestamp 上次登录的时间
|
||||
* @property string $remarks 备注(保留字段)
|
||||
* @property string $originToken Account 中的 Token/SessionId
|
||||
*
|
||||
* @property UserWechat $wechatInfo
|
||||
* @property int $secureLevel 安全等级
|
||||
*/
|
||||
class User extends JsonResource
|
||||
{
|
||||
use ClassicResponse;
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return parent::toArray();
|
||||
}
|
||||
}
|
||||
45
src/Resource/UserWechat.php
Normal file
45
src/Resource/UserWechat.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* UserWechat.php@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/27
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Resource;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Resource\Json\JsonResource;
|
||||
use Singularity\HDK\Core\Resource\ClassicResponse;
|
||||
|
||||
/**
|
||||
* 用户的微信信息
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $userId
|
||||
* @property string $openId
|
||||
* @property string $nickname
|
||||
* @property int $sex 性别。1为男性,2为女性
|
||||
* @property string $province 个人资料填写的省份
|
||||
* @property string $city 个人资料填写的城市
|
||||
* @property string $country 国家,如中国为CN
|
||||
* @property string $privilege 用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
|
||||
* @property string $unionId
|
||||
* @property Carbon $createdAt
|
||||
* @property Carbon $updatedAt
|
||||
*/
|
||||
class UserWechat extends JsonResource
|
||||
{
|
||||
use ClassicResponse;
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return parent::toArray();
|
||||
}
|
||||
}
|
||||
43
src/Sdk/AddressApi.php
Normal file
43
src/Sdk/AddressApi.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Sdk;
|
||||
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Singularity\HDK\Core\Service\HttpRequestService;
|
||||
|
||||
class AddressApi extends HttpRequestService
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
[
|
||||
'headers' => [
|
||||
'Accept-Language' => $this->translator->getLocale()
|
||||
]
|
||||
],
|
||||
config('common.http_request.account.api_base_uri')
|
||||
);
|
||||
}
|
||||
|
||||
public function getStates($code)
|
||||
{
|
||||
return $this->get("countries/$code/states");
|
||||
}
|
||||
|
||||
public function getCities($code, $stateId)
|
||||
{
|
||||
return $this->get("countries/$code/states/$stateId/cities");
|
||||
}
|
||||
|
||||
public function getAreas($code, $stateId, $cityId)
|
||||
{
|
||||
return $this->get("countries/$code/states/$stateId/cities/$cityId/counties");
|
||||
}
|
||||
}
|
||||
64
src/Sdk/AddressRpc.php
Normal file
64
src/Sdk/AddressRpc.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Account\Services\Http;
|
||||
|
||||
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Singularity\HDK\Utils\Service\HttpRequestService;
|
||||
|
||||
class AddressRpc extends HttpRequestService
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
[
|
||||
'headers' => [
|
||||
'Accept-Language' => $this->translator->getLocale()
|
||||
]
|
||||
],
|
||||
config('common.http_request.account.rpc_base_uri')
|
||||
);
|
||||
}
|
||||
|
||||
public function list($uid)
|
||||
{
|
||||
return $this->get('address?uid=' . $uid);
|
||||
}
|
||||
|
||||
public function create($uid, $data)
|
||||
{
|
||||
return $this->post('address/create?uid='. $uid, $data);
|
||||
}
|
||||
|
||||
public function update($uid, $addrId, $data)
|
||||
{
|
||||
return $this->post('address/update?uid=' . $uid . '&addr-id=' . $addrId, $data);
|
||||
}
|
||||
|
||||
public function del($uid, $addrId)
|
||||
{
|
||||
return $this->post('address/delete?uid=' . $uid . '&addr-id=' . $addrId);
|
||||
}
|
||||
|
||||
public function getDefault($uid)
|
||||
{
|
||||
return $this->get('address/getDefault?uid=' . $uid);
|
||||
}
|
||||
|
||||
public function setDefault($uid, $addrId)
|
||||
{
|
||||
return $this->post('address/setDefault', ['uid' => $uid, 'addrId' => $addrId]);
|
||||
}
|
||||
|
||||
public function detail($addrId)
|
||||
{
|
||||
return $this->get('address/detail?addr-id=' . $addrId);
|
||||
}
|
||||
}
|
||||
39
src/Sdk/UserApi.php
Normal file
39
src/Sdk/UserApi.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Account\Services\Http;
|
||||
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Singularity\HDK\Utils\Service\HttpRequestService;
|
||||
|
||||
class UserApi extends HttpRequestService
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
[
|
||||
'Accept-Language' => $this->translator->getLocale()
|
||||
],
|
||||
config('common.http_request.account.api_base_uri')
|
||||
);
|
||||
}
|
||||
|
||||
public function sendCode($params)
|
||||
{
|
||||
return $this->post('secure-code', $params);
|
||||
}
|
||||
|
||||
public function checkCode($params)
|
||||
{
|
||||
$code = $params['code'];
|
||||
unset($params['code']);
|
||||
|
||||
return $this->get('secure-code/' . $code, $params);
|
||||
}
|
||||
}
|
||||
60
src/Sdk/UserRpc.php
Normal file
60
src/Sdk/UserRpc.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Services\Http;
|
||||
|
||||
|
||||
use Hyperf\Contract\TranslatorInterface;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Singularity\HDK\Utils\Service\HttpRequestService;
|
||||
|
||||
class UserRpc extends HttpRequestService
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
[
|
||||
'headers' => [
|
||||
'Accept-Language' => $this->translator->getLocale()
|
||||
]
|
||||
],
|
||||
config('common.http_request.account.rpc_base_uri')
|
||||
);
|
||||
}
|
||||
|
||||
public function alive($name)
|
||||
{
|
||||
return $this->get('user/alive?username=' . urlencode($name));
|
||||
}
|
||||
|
||||
public function detail($uid)
|
||||
{
|
||||
return $this->get('user/detail?uid=' . $uid);
|
||||
}
|
||||
|
||||
public function create($data)
|
||||
{
|
||||
return $this->post('user/create', $data);
|
||||
}
|
||||
|
||||
public function update($uid, $data)
|
||||
{
|
||||
return $this->post('user/update?uid=' . $uid, $data);
|
||||
}
|
||||
|
||||
public function list($uid_arr)
|
||||
{
|
||||
return $this->post('user', $uid_arr);
|
||||
}
|
||||
|
||||
public function checkNamePass($data)
|
||||
{
|
||||
return $this->post('user/signIn', $data);
|
||||
}
|
||||
|
||||
}
|
||||
131
src/Services/AppAuthentication.php
Normal file
131
src/Services/AppAuthentication.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Services;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Hyperf\Redis\Redis;
|
||||
use RedisException;
|
||||
use Singularity\HDK\Auth\Resource\User;
|
||||
use Singularity\HDK\Core\Constants\CommonErrorCode;
|
||||
use Singularity\HDK\Core\Exceptions\ValidateException;
|
||||
|
||||
/**
|
||||
* Singularity\HDK\Auth\Services\AppAuthentication@HyperfAuth
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
class AppAuthentication implements AuthenticationInterface
|
||||
{
|
||||
private $prefix;
|
||||
|
||||
private $expire;
|
||||
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @Inject()
|
||||
* @var RequestInterface
|
||||
*/
|
||||
private RequestInterface $request;
|
||||
|
||||
/**
|
||||
* @Inject()
|
||||
* @var Redis
|
||||
*/
|
||||
private Redis $redis;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = config('common.token.app');
|
||||
$this->prefix = $config['prefix_key'];
|
||||
$this->expire = $config['expire_time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user
|
||||
* @return string
|
||||
* @throws RedisException
|
||||
*/
|
||||
public function generate($user): string
|
||||
{
|
||||
$token = md5(uniqid((string)mt_rand(), true));
|
||||
$this->redis->set($this->prefix . $token, serialize($user), $this->expire);
|
||||
$this->user = $user;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function verified(string $token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
throw new ValidateException(CommonErrorCode::AUTH_APP_ERROR, 'token', $token);
|
||||
}
|
||||
|
||||
$redis_data = $this->redis->get($this->prefix . $token);
|
||||
if (empty($redis_data)) {
|
||||
throw new ValidateException(CommonErrorCode::AUTH_APP_ERROR, 'token', $token);
|
||||
}
|
||||
|
||||
$user = unserialize($redis_data);
|
||||
$this->user = $user;
|
||||
|
||||
$this->redis->expire($this->prefix . $token, $this->expire);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function parseTokenFromHeaders(): ?string
|
||||
{
|
||||
$token = $this->request->getHeaderLine('Authorization');
|
||||
var_dump('header token: ' . $token);
|
||||
|
||||
return $token ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $column
|
||||
* @param bool $returnNull
|
||||
* @return User|string|int|null|array
|
||||
*/
|
||||
public function getCurrentUser(?string $column = null, bool $returnNull = false)
|
||||
{
|
||||
if (!empty($column)) {
|
||||
return $this->user[$column];
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $clearAll
|
||||
* @return mixed
|
||||
*/
|
||||
public function invalid(bool $clearAll = false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uid
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidByUser(string $uid): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidByToken(string $token): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
70
src/Services/AuthenticationInterface.php
Normal file
70
src/Services/AuthenticationInterface.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Singularity\HDK\Auth\Services;
|
||||
|
||||
use Singularity\HDK\Auth\Resource\User;
|
||||
|
||||
interface AuthenticationInterface
|
||||
{
|
||||
/**
|
||||
* 生成 token
|
||||
*
|
||||
* @param \Singularity\HDK\Auth\Resource\User $user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate(User $user): string;
|
||||
|
||||
/**
|
||||
* 将传入的 token 进行校验
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function verified(string $token);
|
||||
|
||||
/**
|
||||
* 从 Header 中获取 Token
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function parseTokenFromHeaders(): ?string;
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的用户信息
|
||||
*
|
||||
* @param string|null $column 要获取的单个字段
|
||||
* @param bool $returnNull 开启后不抛出异常,而是返回 null
|
||||
*
|
||||
* @return \Singularity\HDK\Auth\Resource\User|string|int|null|array
|
||||
*/
|
||||
public function getCurrentUser(?string $column = null, bool $returnNull = false);
|
||||
|
||||
/**
|
||||
* 作废此 Token
|
||||
*
|
||||
* @param bool $clearAll
|
||||
*
|
||||
* @deprecated
|
||||
* @uses \Singularity\HDK\Auth\AuthenticationInterface::invalidByToken()
|
||||
* @uses \Singularity\HDK\Auth\AuthenticationInterface::invalidByUser()
|
||||
*/
|
||||
public function invalid(bool $clearAll = false);
|
||||
|
||||
/**
|
||||
* 通过用户作废 Token
|
||||
*
|
||||
* @param string $uid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidByUser(string $uid): bool;
|
||||
|
||||
/**
|
||||
* 作废此 token
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function invalidByToken(string $token): bool;
|
||||
}
|
||||
184
src/Services/JwtAuthentication.php
Normal file
184
src/Services/JwtAuthentication.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/** @noinspection SpellCheckingInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Services;
|
||||
|
||||
use Dont\JustDont;
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\JWT;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Hyperf\Server\Exception\InvalidArgumentException;
|
||||
use Singularity\HDK\Auth\Resource\JsonWebToken;
|
||||
use Singularity\HDK\Auth\Resource\User;
|
||||
use Singularity\HDK\Auth\Traits\CannotInvalid;
|
||||
use Singularity\HDK\Core\Enumerations\Http\Header\RFCs\RFC7616;
|
||||
use Singularity\HDK\Core\Exceptions\Unauthorized;
|
||||
use Singularity\HDK\Core\Constants\CommonErrorCode;
|
||||
use Singularity\HDK\Core\Exceptions\ValidateException;
|
||||
|
||||
/**
|
||||
* Singularity\HyperfDevelopmentKit\Account\Services\Auth\JwtAuthentication@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/27
|
||||
*/
|
||||
class JwtAuthentication implements AuthenticationInterface
|
||||
{
|
||||
use JustDont;
|
||||
use CannotInvalid;
|
||||
|
||||
/**
|
||||
* @Inject()
|
||||
* @var \Hyperf\HttpServer\Contract\RequestInterface
|
||||
*/
|
||||
private RequestInterface $request;
|
||||
|
||||
public function generate(User $user): string
|
||||
{
|
||||
$expireTime = config('common.token.jwt.expire_time');
|
||||
$jwt = [
|
||||
// 设置限定条件
|
||||
'iss' => config('idp_id'),
|
||||
'sub' => '',
|
||||
'aud' => config('idp_id'),
|
||||
'iat' => microtime(true),
|
||||
'nbf' => microtime(true),
|
||||
'exp' => microtime(true) + $expireTime,
|
||||
];
|
||||
|
||||
// 绑定当前用户信息
|
||||
$jwt = $jwt + $user->toArray();
|
||||
|
||||
// 加密
|
||||
return JWT::encode(
|
||||
payload: $jwt,
|
||||
key: config('common.token.jwt.private_key'),
|
||||
alg: 'EdDSA'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码,并返回验证后的值
|
||||
*/
|
||||
public function verified(?string $token = null): JsonWebToken
|
||||
{
|
||||
if (!empty(Context::get('jwt'))) {
|
||||
return Context::get('jwt');
|
||||
}
|
||||
if (empty($token)) {
|
||||
throw new ValidateException(CommonErrorCode::AUTH_JWT_ERROR, 'token', $token);
|
||||
}
|
||||
|
||||
JWT::$leeway = 30;
|
||||
try {
|
||||
$decoded = (array)JWT::decode($token, config('common.token.jwt.public_key'));
|
||||
} catch (ExpiredException $exception) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_EXP_TIMEOUT;
|
||||
throw new Unauthorized($error_code, $exception);
|
||||
}
|
||||
if (empty($decoded)) {
|
||||
throw new ValidateException(CommonErrorCode::AUTH_JWT_ERROR, 'token', $decoded);
|
||||
}
|
||||
|
||||
// 判断签发机构
|
||||
if (($decoded['iss'] ?? '') !== config('idp_id')) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_ISS_ERROR;
|
||||
throw new Unauthorized($error_code);
|
||||
}
|
||||
|
||||
// 判断签发时间
|
||||
if (($decoded['iat'] ?? 0) > microtime(true)) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_IAT_ERROR;
|
||||
throw new Unauthorized($error_code);
|
||||
}
|
||||
|
||||
// 判断生效时间
|
||||
if (($decoded['nbf'] ?? 0) > microtime(true)) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_NBF_ERROR;
|
||||
throw new Unauthorized($error_code);
|
||||
}
|
||||
|
||||
// 判断过期时间
|
||||
if (($decoded['exp'] ?? 0) <= microtime(true)) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_EXP_TIMEOUT;
|
||||
throw new Unauthorized($error_code);
|
||||
}
|
||||
|
||||
if (empty($decoded['uid'])) {
|
||||
$error_code = CommonErrorCode::AUTH_JWT_UID_ERROR;
|
||||
throw new Unauthorized($error_code);
|
||||
}
|
||||
|
||||
$jwt = new JsonWebToken($decoded);
|
||||
Context::set('jwt', $jwt);
|
||||
|
||||
return $jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中解析出 token.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function parseTokenFromHeaders(): ?string
|
||||
{
|
||||
$token = $this->request->getHeaderLine('Authorization');
|
||||
$token = (
|
||||
empty($token)
|
||||
|| !is_string($token)
|
||||
|| strlen($token) <= 0
|
||||
|| !str_starts_with($token, 'Bearer ')
|
||||
)
|
||||
? null
|
||||
: substr($token, 7);
|
||||
if ($token === false || $token === 'null') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param string|null $column
|
||||
* @param bool $returnNull
|
||||
* @param bool $redirectReturn
|
||||
*
|
||||
* @return \Singularity\HDK\Auth\Resource\User|string|int
|
||||
*/
|
||||
public function getCurrentUser(
|
||||
?string $column = null,
|
||||
bool $returnNull = false,
|
||||
bool $redirectReturn = false
|
||||
) {
|
||||
// 惰性查询当前用户信息
|
||||
/** @var \Singularity\HDK\Auth\Resource\JsonWebToken $currentUser */
|
||||
$currentUser = Context::get('jwt');
|
||||
if (empty($currentUser)) {
|
||||
throw new Unauthorized(RFC7616::DIGEST);
|
||||
}
|
||||
|
||||
if (isset($column)) {
|
||||
if (!isset($currentUser[$column])) {
|
||||
throw new InvalidArgumentException('属性不存在');
|
||||
}
|
||||
|
||||
return $currentUser[$column];
|
||||
}
|
||||
return $currentUser;
|
||||
}
|
||||
}
|
||||
192
src/Services/SessionAuthentication.php
Normal file
192
src/Services/SessionAuthentication.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Services;
|
||||
|
||||
use Dont\JustDont;
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\HttpMessage\Cookie\Cookie;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Hyperf\Redis\Redis;
|
||||
use Hyperf\Server\Exception\InvalidArgumentException;
|
||||
use Singularity\HDK\Account\Services\Auth\AuthenticationInterface;
|
||||
use Singularity\HDK\Auth\Resource\User;
|
||||
use Singularity\HDK\Utils\Constants\CommonErrorCode;
|
||||
use Singularity\HDK\Utils\Exceptions\Unauthorized;
|
||||
use Singularity\HDK\Utils\Exceptions\ValidateException;
|
||||
|
||||
class SessionAuthentication implements AuthenticationInterface
|
||||
{
|
||||
use JustDont;
|
||||
|
||||
private string $lastInvalidateTimeKey;
|
||||
|
||||
public function __construct(
|
||||
private SessionInterface $session,
|
||||
private RequestInterface $request,
|
||||
private Redis $redis,
|
||||
) {
|
||||
$redis_path = config('common.redis.prefix', '');
|
||||
$last_invalidate_time_key = config('common.token.session.forbidden_key', 'user:last_invalidate_time');
|
||||
$this->lastInvalidateTimeKey = $redis_path . $last_invalidate_time_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function generate(User $user): string
|
||||
{
|
||||
$this->session->set('userInfo', $user->toArray());
|
||||
$this->session->set('createdAt', microtime(true));
|
||||
|
||||
return $this->session->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码,并返回验证后的值
|
||||
*/
|
||||
public function verified(?string $token = null): User
|
||||
{
|
||||
if (!$this->session->isValidId($token ?? '')) {
|
||||
throw new ValidateException(CommonErrorCode::AUTH_SESSION_ERROR, 'token', $token);
|
||||
}
|
||||
|
||||
$user = $this->session->get('userInfo');
|
||||
if (empty($user)) {
|
||||
throw new Unauthorized(CommonErrorCode::AUTH_SESSION_ERROR);
|
||||
}
|
||||
$user = new User($user);
|
||||
|
||||
if (empty($user['uid'])) {
|
||||
throw new Unauthorized(CommonErrorCode::AUTH_SESSION_UID_ERROR);
|
||||
}
|
||||
|
||||
// 判断用户 session 是否应该失效
|
||||
$last_invalidate_time = $this->redis->hGet(
|
||||
$this->lastInvalidateTimeKey,
|
||||
$user['uid']
|
||||
);
|
||||
/**
|
||||
* @link SessionAuthentication::invalid(true)
|
||||
*/
|
||||
if ($this->session->get('createdAt') < $last_invalidate_time) {
|
||||
throw new Unauthorized(CommonErrorCode::AUTH_SESSION_CREATED_AT_ERROR);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalid(
|
||||
bool $clearAll = false,
|
||||
): Cookie {
|
||||
if ($clearAll) {
|
||||
$user = $this->session->get('userInfo');
|
||||
$user = new User($user);
|
||||
$this->redis->hSet(
|
||||
$this->lastInvalidateTimeKey,
|
||||
$user['uid'] ?? '',
|
||||
microtime(true)
|
||||
);
|
||||
}
|
||||
$this->session->invalidate();
|
||||
|
||||
return new Cookie(
|
||||
'is_login',
|
||||
'',
|
||||
time() - 3600,
|
||||
'/',
|
||||
domain: $this->request->getUri()->getHost(),
|
||||
httpOnly: false,
|
||||
sameSite: Cookie::SAMESITE_LAX
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中解析出 token.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function parseTokenFromHeaders(): ?string
|
||||
{
|
||||
$session_name = config('session.options.session_name');
|
||||
$token = $this->request->getCookieParams()[$session_name] ?? null;
|
||||
return (
|
||||
empty($token)
|
||||
|| !is_string($token)
|
||||
|| strlen($token) <= 0
|
||||
|| $token === 'null'
|
||||
)
|
||||
? null
|
||||
: $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getCurrentUser(
|
||||
?string $column = null,
|
||||
bool $returnNull = false,
|
||||
bool $redirectReturn = true,
|
||||
): User|string|int|null {
|
||||
// 查询是否已通过中间件鉴权
|
||||
$user = $this->session->get('userInfo');
|
||||
|
||||
// 未匹配到任何用户信息
|
||||
if (empty($user)) {
|
||||
if ($returnNull) {
|
||||
return null;
|
||||
}
|
||||
throw new Unauthorized(CommonErrorCode::AUTH_SESSION_ERROR);
|
||||
}
|
||||
|
||||
// 已匹配到时返回指定字段或整个用户信息
|
||||
if ($column !== null) {
|
||||
if (!isset($user[$column])) {
|
||||
if ($returnNull) {
|
||||
return null;
|
||||
}
|
||||
throw new InvalidArgumentException("属性 $column 不存在");
|
||||
}
|
||||
|
||||
return $user[$column];
|
||||
}
|
||||
return new User($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalidByUser(string $uid): bool
|
||||
{
|
||||
$result = $this->redis->hSet(
|
||||
$this->lastInvalidateTimeKey,
|
||||
$uid,
|
||||
microtime(true)
|
||||
);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalidByToken(string $token = ''): bool
|
||||
{
|
||||
if ($token === '') {
|
||||
return $this->session->invalidate();
|
||||
}
|
||||
$result = $this->redis->del($token);
|
||||
return $result > 0;
|
||||
}
|
||||
}
|
||||
46
src/Traits/CannotInvalid.php
Normal file
46
src/Traits/CannotInvalid.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* CannotInvalid.php@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/28
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Traits;
|
||||
|
||||
/**
|
||||
* 用于无法作废token的鉴权
|
||||
* Singularity\HDK\Account\Services\Auth\CannotInvalid@hyperf-development-kit
|
||||
*
|
||||
* @author 李东云<dongyun.li@luxcreo.cn>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2022/4/28
|
||||
*/
|
||||
trait CannotInvalid
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalid(bool $clearAll = false): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalidByUser(string $uid): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function invalidByToken(string $token): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
25
src/Traits/LoginOptional.php
Normal file
25
src/Traits/LoginOptional.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* LoginOptional.php@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Traits;
|
||||
|
||||
/**
|
||||
* 无需登录的处理
|
||||
* Singularity\HDK\Account\Traits\LoginOptional@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
trait LoginOptional
|
||||
{
|
||||
public function authorize(): bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
37
src/Traits/LoginRequired.php
Normal file
37
src/Traits/LoginRequired.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* LoginRequired.php@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*/
|
||||
|
||||
namespace Singularity\HDK\Auth\Traits;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\Validation\Request\FormRequest;
|
||||
use Singularity\HDK\Auth\Services\AuthenticationInterface;
|
||||
|
||||
/**
|
||||
* Singularity\HDK\Account\Traits\LoginRequired@HDK
|
||||
*
|
||||
* @author 李东云 <Dongyun.Li@LuxCreo.Ai>
|
||||
* Powered by PhpStorm
|
||||
* Created on 2023/1/17
|
||||
*
|
||||
* @mixin FormRequest
|
||||
*/
|
||||
trait LoginRequired
|
||||
{
|
||||
/**
|
||||
* @Inject()
|
||||
* @var AuthenticationInterface
|
||||
*/
|
||||
private AuthenticationInterface $authentication;
|
||||
|
||||
public function authorize(): bool {
|
||||
// 登录逻辑
|
||||
return !!$this->authentication->getCurrentUser();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user